Partage
  • Partager sur Facebook
  • Partager sur Twitter

Fichier.h et fichier.c

22 janvier 2018 à 13:24:45

Bonjour tout le monde. Bien que j'ai lu la leçon sur les #include , j'ai du mal à comprendre ou créer ces fichiers.

Dois-je inclure en haut du "main"? Et les exercices sur les tableaux, dois tous les faire sur le même "main" à la suite ?

Ou bien dois-je créer d'autres fichiers pour chaque exercice de tableau et inclure à chaque fois ? Et où dois-je les inclure en fait? Sur le "main"?

  • Partager sur Facebook
  • Partager sur Twitter
22 janvier 2018 à 13:57:00

Comme tu le sens.
tu peux créer un projet par exercice, ou bien une fonction par exercice et tout tester dans un seul main.
etc...

Edit, si c'est pour être corrigé via le site, je ne connais pas comment ça se goupille, à voir ce que l'on te permet de faire.

-
Edité par neuneutrinos 22 janvier 2018 à 13:57:54

  • Partager sur Facebook
  • Partager sur Twitter
22 janvier 2018 à 14:16:52


Exemple : Main.c

#include <stdio.h>
#include <stdlib.h>

#include "position.h";

int main(int argc, char const *argv[]) {
  position();

  return 0;
}

Dans main.c je fais appel à une fonction comme ceci : (pour positionner mon joueur)

position();

Le programme vas donc chercher le prototype de la fonction, il ne trouve pas ce dernier dans main.c il vas donc fouiller dans les #include

J'ai donc introduit position.h et ce dernier contient :

void position(void);

Enfin, position.c contient :

#include "position.h"

void positionPnj() {
  PositionneLeJoueur;
  return;
}

Ainsi, position.h contient les prototypes des fonctions de positionnement et position.c contient les fonctions en question. j'ai donc introduit position.h dans main.c et position.c



-
Edité par Sceau de Nîmes 22 janvier 2018 à 14:17:05

  • Partager sur Facebook
  • Partager sur Twitter

J'ai demandé à Google mais il est sous assistance respiratoire à cause du Corona.

J’ai pas besoin que tu crois ce que moi je crois.

22 janvier 2018 à 14:17:45

Généralement un fichier = une catégorie de fonctions.

Prenons un programmes d'un jeu quelconque. Tu auras un fichier pour le main. Un fichier pour les fonctions "utiles", un fichier pour les fonctions qui gèrent les fichiers de sauvegarde, un autre pour les fonctions d'interractions avec l'utilisateur, un autre pour les fonctions d'affichage, etc

Les includes sont font tout en haut de fichier, du plus importants au moins importants. On include uniquement les header des fichiers contenant les fonctions dont on a besoin. Par exemple d'habitude tu includes <stdio.h> et <stdlib.h> parceque ce sont les header des fichiers contenant des fonctions telles que `printf`, `scanf`, `atoi`, etc

Si tu as un programme qui regroupe plusieurs exercices, il est courant de faire un menu dans le main genre :

[1] exo 1
[2] exo 2
etc

Et tu peux avoir un fichier par exo (mais attention, toujours un seul main dans tout ton code).

-
Edité par Insonore 22 janvier 2018 à 14:19:03

  • Partager sur Facebook
  • Partager sur Twitter
Hugo
Anonyme
22 janvier 2018 à 17:04:29

Chevalier Numérique a écrit:

Dans main.c je fais appel à une fonction comme ceci : (pour positionner mon joueur)

position();

Le programme vas donc chercher le prototype de la fonction, il ne trouve pas ce dernier dans main.c il vas donc fouiller dans les #include

Edité par Chevalier Numérique about an hour ago


Euh, non, c'est inexact (enfin, faux, pour être exact).

Il faut comprendre comment fonctionne la "compilation", et par "compilation" j'entends preprocesseur +  compilation + le linkage.

Les instructions de préprocesseur sont exécuté AVANT le compilateur.
Toute les instruction de préprocesseur sont évalue et font leurs jobs.
Par exemples, "#include fichier" permet de copier le contenue de "fichier" à la place de #include.
Ca remplace la ligne par le contenue du fichier, carrément.

Ensuite, la compilation (qui ne s'effectue QUE sur les fichiers .c).
À chaque fois que le compilateur commence à un compiler un fichier, il "oublie" tout : il ne connaît plus les fonctions, les variables, etc etc.
Il part du haut du fichier, et descend ligne par ligne en faisant ses vérifications (syntaxique, sémantique, etc etc).
Quand il tombe sur le prototype d'une fonction, il "retient" cette dernière en mémoire (son nom, son type de retour, ses arguments ...).
Quand il tombe sur un appel de fonction, il regarde dans sa mémoire s'il la connaît.
Si oui, pas de soucis (a moins que mauvais nombre d'argument, retour mal utilisé, mais ça c'est la vérification syntaxique et sémantique).
Si non, il va émettre un avertissement de type "implicite declaration of function 'untel'".

Mais il est possible que cela n'arrête pas la compilation : le compilateur va considérer que puisqu'il ne la connaît pas, c'est une fonction qui retourne un int et qui prends aucun arguments, et si d'aventure c'est correct sémantiquement (par exemple, la variable qui récupère le retour de la fonction est "compatible" avec un int, c'est ok), alors la compilation se poursuivra.

Ensuite, une fois qu'il a compilé tout les .c, le travail du vrai compilateur est terminé.
C'est au tour du linker. Le linker va prendre tout les .o (c'est les .c compilés) et les assembler.
C'est a ce moment que le linker va vérifier si chaque fonctions utilisé dans les .c existe.
Typiquement, tu as la fonction "position" défini dans "position.c", et utilisé dans "main.c".
le linker va vérifier que la fonction "position" appelé dans "main.c" existe bien et en un seul exemplaire.
Si ce n'est pas le cas, il émettra un erreur (et la ça arrête tout) de type "undefined reference to 'untel'".

Pour que le compilateur puisse connaitre les prototype de fonction dans chaque .c, on déclare simplement le prototype dans un .h que l'on inclue au debut du fichier .c.
Ainsi, à l'étape du préprocesseur, le contenue du .h (et donc le prototype de la fonction) est copié au debut du .c, à l'étape de compilation, le compilateur va connaitre la fonction (et n'émettra pas de warning), et le linker sera content.


Tout cela pour dire : Non, le "programme" ne vas jamais chercher le prototype : soit il le connaît, soit il émet un warning, et ensuite un #include n'est jamais fouillé, il est remplacé au début, avant même la compilation.

Sinon le schéma est plutôt bon.

-
Edité par Anonyme 23 janvier 2018 à 9:50:07

  • Partager sur Facebook
  • Partager sur Twitter
22 janvier 2018 à 21:53:07

Merci pour vos réponses. Je peux faire plusieurs fonctions dans le "main"?
  • Partager sur Facebook
  • Partager sur Twitter
22 janvier 2018 à 22:04:00

Hello,

DavidBerger2 a écrit:

Je peux faire plusieurs fonctions dans le "main"?

Mais oui.

  • Partager sur Facebook
  • Partager sur Twitter

On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

22 janvier 2018 à 22:49:42

d'accord . je vous présente ce que j'ai fait : 

#include <stdio.h>

#include <stdlib.h>

#include "tableau.h"

int main()

{

    int sommeTableau = 0 , tableau[4] = {10,20,20,30} ;

      int somme = 0 , i ;

    for (i=0 ; i<4 ; i++)

    {

      sommeTableau+=tableau[i];

    }

    printf ("%d\n" ,sommeTableau );

    return 0;

}

int main()

{

    int tableau[4],sommeTableau = 0,moyenneTableau = 0;

    int i=0;

    tableau[0] = 10;

    tableau[1] = 20;

    tableau[2] = 30;

    tableau[3] = 40;

    for(i=0 ; i<4 ;i++)

    {

        sommeTableau+=tableau[i];

        moyenneTableau=sommeTableau/4;

    }

    printf("%d\n",moyenneTableau);

    return 0;

}

mais lors de la compil  y a un probleme, je ne sais pas lequel . chaque fonction fonctionne séparément mais lorsque je mets les 2 ensemble , y a un probleme.
  • Partager sur Facebook
  • Partager sur Twitter
22 janvier 2018 à 22:59:19

edgarjacobs a écrit:

Hello,

DavidBerger2 a écrit:

Je peux faire plusieurs fonctions dans le "main"?

Mais oui.

Ce qui ne veut pas dire que tu peux avoir plusieurs fonctions main(), mais que main.c peut avoir plusieurs fonctions

#include ....

void f2(void) {

	....
	
	....
	
}

void f1(void) {

	....
	
	f2();
	
	....
	
}

int main(void) {

	....
	
	f1();
	
	....
	
	return(0);
}



-
Edité par edgarjacobs 22 janvier 2018 à 23:01:03

  • Partager sur Facebook
  • Partager sur Twitter

On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

23 janvier 2018 à 4:29:19

Tu as mis le int main en dernier? Peu importe le sens?

Tu as mis obligatoirement des voies où tu peux mettre autre chose?

Dans mon exemple , les fonctions manipulent des chiffres.

  • Partager sur Facebook
  • Partager sur Twitter
23 janvier 2018 à 6:03:56

* "y a un problème". C'est bien triste. Mais si tu ne le décris pas ...

* Pour éviter les ennuis avec les fichiers ".h", il faut prendre la bonne habitude d'y mettre des "gardes" pour empêcher d'éventuelles inclusions multiples.

Le rôle d'un fichier .h est en général de fournir les entêtes (d'où le .h de header) de fonctions d'un "module", c'est à dire un ensemble de fonctions qui vont ensemble. Plus les types, les constantes etc.

Exemple (fichier actions.h)

#ifndef ACTIONS_H
#define ACTIONS_H


void dormir(int duree);
void manger(char plat[], float poids);


#endif

sachant que ces éléments sont définis dans un source "actions.c" destiné à être compilé séparément, et qui inclut aussi actions.h

#include "actions.h"

void dormir(int duree) {
  ...
}

// etc.

La raison de compiler séparément, c'est qu'on envisage de faire de ce module une bibliothèque, compilée une fois pour toutes, utilisée dans plusieurs programmes.

---

Or il arrive qu'une bibliothèque A en emploie une autre B. Donc A.h contient un "#include B.h". Et si on emploie C qui inclut aussi B, indirectement ça va mener à inclure plusieurs fois les mêmes éléments, donc à des redéfinitions qui vont faire tousser le compilateur.

D'où la précaution consistant à encadrer systématiquement le contenu du fichier .h

// fichier A.h

#ifndef A_H
#define A_H





#endif




-
Edité par michelbillaud 23 janvier 2018 à 6:06:09

  • Partager sur Facebook
  • Partager sur Twitter
23 janvier 2018 à 8:45:38

SofEvans2 J'ai tout lus, un beau rappel merci
  • Partager sur Facebook
  • Partager sur Twitter

J'ai demandé à Google mais il est sous assistance respiratoire à cause du Corona.

J’ai pas besoin que tu crois ce que moi je crois.

Anonyme
23 janvier 2018 à 10:00:48

@Chevalier Numérique

C'est vrai que c'est assez ... "chiant" ... de devoir savoir ce genre de détail, mais cela fait partie d'un bagage de connaissance que je suis bien content d'avoir quand il s'agit de comprendre pourquoi la compilation a échoué.
C'est d'autant plus important de savoir cela car on présente gcc comme étant un compilateur C, ce qui n'est pas du tout le cas.

gcc est un "front-end" qui se charge en réalité de toute la partie "pre-processeur / compilation / linkage" (entre autre, car si on veut faire une bibliothèque statique / dynamique, c'est un peu différent).

cpp = preprocesseur C
cc1 = compilateur C
cc1++ = compilateur C++
ld = linker
(et autre, style gdb pour le debug, ar pour les biblitohèque ...)

Et gcc appel cc1 OU cc1++ en fonction de certains paramètre, comme par exemple l'extension des fichiers.
Si ton fichier est un .c, alors ce sera cc1 qui sera utilisé, si c'est .cpp, alors c'est cc1++.
Et en C++, le nom des fonctions est manglé (le nom de la fonction est changé selon les paramètre de la fonction, afin de supporter la surcharge).
C'est pour cela que la plupart des débutants qui font correctement les choses avec les .c / .h finissent tout de même par avoir des erreur, car l'un de leur fichier  (ou plusieurs) est un .cpp, qui est donc compilé en C++ et non en C.

  • Partager sur Facebook
  • Partager sur Twitter
23 janvier 2018 à 10:08:50

Oui, en plus c'est dis dans le cours de Matéo, ils dis c'est la différence entre un programmeur lambda et un vrai en gros, là je suis occupé je te lis dès que libre ^^
  • Partager sur Facebook
  • Partager sur Twitter

J'ai demandé à Google mais il est sous assistance respiratoire à cause du Corona.

J’ai pas besoin que tu crois ce que moi je crois.

23 janvier 2018 à 12:42:21

Ben le problème c'est quand j'ai essayé de compiler les 2 fonctions successives dans le même "main" ça n'a pas fait la compilation il y avait un carré rouge en face du 2e "main" donc c'était un problème.
  • Partager sur Facebook
  • Partager sur Twitter
23 janvier 2018 à 12:56:44

DavidBerger2 a écrit:

Ben le problème c'est quand j'ai essayé de compiler les 2 fonctions successives dans le même "main" ça n'a pas fait la compilation il y avait un carré rouge en face du 2e "main" donc c'était un problème.

Tu ne peux pas avoir plusieurs fonctions avec le même nom en C. Mais tu peux avoir plusieurs fonctions, avec des noms différents, dans un même fichier.



  • Partager sur Facebook
  • Partager sur Twitter
Hugo
23 janvier 2018 à 12:58:48

Tu peux avoir deux fonctions dans le même fichier main, mais un programme doit avoir une seule fonction main. C’est le point d’entrée du programme, s’il y en a deux comment savoir où le programme commence ?

  • Partager sur Facebook
  • Partager sur Twitter
Tutoriel Ruby - Bon tutoriel C - Tutoriel SDL 2 - Python avancé - Faîtes un zeste, devenez des zesteurs
Anonyme
23 janvier 2018 à 13:12:05

Je sais que mon explications est un gros pavé plutôt abstrait, mais comme je l'ai dit avant, le linker va se charger de vérifier que chaque fonction appelée existe en un seul exemplaire.

Si tu as 0 occurence de la fonction, le linker te dira "undefined reference", et si tu as en plusieurs, le linker te dire "multiple definition of".

Une fonction ne peut être défini qu'une fois (comprendre : tu ne peux pas avoir le même nom de fonction plusieurs fois, et ce que ce soit dans le même .c ou dans des .c différent).

-
Edité par Anonyme 23 janvier 2018 à 13:18:13

  • Partager sur Facebook
  • Partager sur Twitter
23 janvier 2018 à 14:56:08

Je viens de comprendre merci. Par contre , ou dois-je mettre la condition de définition du programme pour éviter l'inclusion infinie? Dans le ".c" ?
  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
23 janvier 2018 à 15:26:59

La directive de préprocesseur #include copie le contenu du fichier à la place de ladite directive.

Supposons "fichier1"

#include "fichier2"

AAAA
BBBB
CCCC
et "fichier2"

#include "fichier1"

1111
2222
3333
Que se passera-t-il lorsque le preprocesseur va vouloir faire son boulot ?

Il va commencer par "fichier1", et va voir le "#include".
Il va donc faire

avant :

#include "fichier2"

AAAA
BBBB
CCCC
après :

#include "fichier1"

1111
2222
3333

AAAA
BBBB
CCCC

Remarque qu'on est toujours en train de faire "fichier1". Vois-tu le problème arriver ?
Il va voir #include fichier1, il va donc faire

avant :

#include "fichier1"

1111
2222
3333

AAAA
BBBB
CCCC
après :

#include "fichier2"

AAAA
BBBB
CCCC

1111
2222
3333

AAAA
BBBB
CCCC


etc etc ...

Et cela ne s'arrête pas que à cet exemple où le fichier1 inclue le fichier2 et le fichier2 inclue le fichier1.

Je t'ai dit avant que les fonctions devaient être unique (tu ne peux pas avoir deux fonctions "main", comme deux fonction "toto", etc etc).
Mais il en ait de même pour les variables ! Et aussi les enumération (enum) ! Et les structures !

Que se passe-t-il si dans le fichier3 tu as

struct test {
  int a;
  int b;
};

et dans le fichier4 tu as

#include "fichier3"
#include "fichier3" 

?

Après que les préprocesseur ait fait  son taff sur le fichier4, tu auras :

struct test {
  int a;
  int b;
};
struct test {
  int a;
  int b;
};

Hors, dans un même .c, tu viens de declarer deux structure avec le même nom ! (peu importe quelle soient identique à l'intérieur, il y a bien deux déclaration distinctes, donc deux struct distinctes).
Cela va donc déclencher une erreur.

Pour faire en sorte de ne pas avoir cela, on doit protéger les fichiers qui sont inclue via #include.
Ces fichiers, se sont les .h (normalement ... on n'inclue jamais de .c, a moins de savoir exactement pourquoi et d'avoir une excellent raison).
Donc, on doit protéger les .h contre la multi-inclusion.

Comment faire ? Avec les directive de préprocesseur (car il n'y a que le préprocesseur qui fasse quelque chose avant le compilateur).
Et les directive sont

#ifndef PROTECTION
#define PROTECTION

// ... code ...

#endif

#ifndef veut si "si ce n'est pas défini". la première fois, PROTECTION n'est pas défini, donc pas de soucis. Ensuite, le #define permet de définir PROTECTION.
Donc la deuxieme fois, le #ifndef sera faux (vu que PROTECTION sera défini), donc le "// ... code ..." ne sera pas ajouté.

Personnellement, afin d'avoir une garde vraiment unique, je fais de la forme

H_NOMFICHIER_AAAAMMJJ_HHmm_TRI

Avec AAAA = Année
MM = Mois
JJ = Jour
HH = Heure
mm = minute
TRI = trigramme (première lettre prénom + première et seconde lettre nom de famille)

ce qui donne pour "toto.h" :

H_TOTO_20180123_1526_SEV

Si un jour quelqu'un aura défini "ça", j'aurais vraiment, vraiment pas de chance ...


  • Partager sur Facebook
  • Partager sur Twitter
23 janvier 2018 à 15:52:07

Coucou,

@SofEvans2 beau rappel et plutôt complet en plus (je parle du 5e post). Sauf que si le compilateur rencontre un appel de fonction sans avoir vu une (ou plusieurs) déclaration(s) au préalable (prototype ou définition), il ne considérera pas qu’elle ne prend aucun paramètre, mais qu’elle n’a pas de spécification de paramètre, le compilateur autorisera donc n’importe quel nombre de paramètres de n’importe quel type. Cependant ce processus (implicit function declaration) n’existe plus depuis C99, bien que la plupart des compilateurs le réalise encore.

-
Edité par fscorpio 23 janvier 2018 à 15:53:07

  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
23 janvier 2018 à 15:55:22

@fscorpio

Oui, je me suis mal exprimé.
Le compilateur agira comme si la fonction avait ce prototype "int func()" (et non "int func(void)" comme je l'ai laissé sous-entendre, à tord).
  • Partager sur Facebook
  • Partager sur Twitter
23 janvier 2018 à 17:25:59

Merci beaucoup . Donc je dois définir les .h uniquement ou tous les fichiers aussi du coup même si je sais que je vais pas inclure ceux là?
  • Partager sur Facebook
  • Partager sur Twitter
23 janvier 2018 à 17:40:40

Tu dois placer le "code" des fonctions (appelé définition) dans le .c et les prototypes correspondants dans le .h.

Le .c inclut son .h correspondant.
Les autres .c et .h ayant besoin de ces fonctions incluent également ce .h.

Les .c ne sont jamais inclut, cependant, de part la configuration du projet sur ton IDE, les .c seront envoyé au compilateur, inclut ou non. Et seront utilisé différemment que les .h.


En fait, un .c et tous les .h qu’il inclut (et que le .h inclut, et que les .h incluent par le .h incluent, etc.) forme une unité de compilation. Les includes servent à ajouter les .h nécessaire à l’unité de compilation pour être compilé (les prototypes par exemple).

Ton IDE est réglé pour associer à chaque .c une unité de compilation.

Chaque unité de compilation est compilé séparément par le compilateur et donne un fichier objet par unité de compilation.

Enfin, ton IDE envoie tous les fichiers objets au linker pour qu’il réalise le programme final.

-
Edité par fscorpio 23 janvier 2018 à 17:48:42

  • Partager sur Facebook
  • Partager sur Twitter
23 janvier 2018 à 18:52:46

Voilà ce que je fait moi:

Je copie colle manuellement chaque header dans un fichier.h et je fais de même pour chaque fonction , dans un fichier.c voilà.

C'est bien en copiant collant que je dois mettre les prototypes ou les fonctions elles même dans les fichiers concernés?

J'avais oublié de préciser que mon dossier de sauvegarde de mes projets ne se trouvent pas sur codeblocks mais sur mon DD .je l'ai décidé ainsi.

-
Edité par DavidBerger2 24 janvier 2018 à 0:19:14

  • Partager sur Facebook
  • Partager sur Twitter
25 janvier 2018 à 14:09:46

L'inclusion de mes prototypes , je le fais manuellement par copier coller, est ce la bonne méthode ou y a t'il une manipulation à faire par codeblocks ?
  • Partager sur Facebook
  • Partager sur Twitter
25 janvier 2018 à 14:21:51

L'inclusion des prototypes ça se fait en tapant

#include "mesfonctions.h"

avec ses petits doigts dans les codes-sources concernés.

Mais peut-être ce n'était pas la question que vous vous posiez.

-
Edité par michelbillaud 25 janvier 2018 à 14:22:46

  • Partager sur Facebook
  • Partager sur Twitter
Anonyme
25 janvier 2018 à 14:30:10

La règle, c'est d'inclure chaque fichier nécessaire et pas plus (surtout dans les .h).
Que tu tapes #include ou que tu le copie/colle, franchement c'est bonnet blanc ou blanc bonnet.

Maintenant, je sais que certains compilateur te proposes de spécifier un fichier qui sera inclue automatiquement au début de chaque fichier .c.
Pour gcc, de mémoire, c'est "-include fichier".
De cette manière, si tu dois absolument inclure un fichier .h au début de chaque .c et que tu veux être sûr, tu peux utiliser cela.

C'est un peu comme l'option "-D" qui te permet de faire comme si tu avais mis un #define au début de chaque fichier .c.
Mais bon, le mieux, c'est tout de même d'écrire explicitement tes includes : tu ne dépendras pas d'une option exotique d'un compilateur si d'aventures "-include" n'existe pas sous d'autre compilateur.

-
Edité par Anonyme 25 janvier 2018 à 14:30:30

  • Partager sur Facebook
  • Partager sur Twitter
25 janvier 2018 à 14:53:19

Et surtout, les dépendances sont indiquées dans le source.

-
Edité par michelbillaud 25 janvier 2018 à 14:53:30

  • Partager sur Facebook
  • Partager sur Twitter