définir une fonction deux fois en C

J’ai un problème. j’ai écrit ce code, ahac et le main.c:

fichier: ah

#ifndef _a_H #define _a_H int poly (int a, int b, int c, int x); int square (int x) { return x*x; } #endif // _a_H 

fichier: ac

 #include "ah" int poly (int a, int b, int c, int x) { return a*square(x) + b * x +c; } 

fichier: main.c

 #include  #include "ah" int main() { int p1 = poly1 (1 ,2 , 1, 5); int p2 = poly2 (1 ,1 , 3, 5); printf ("p1 = %d, p2 = %d\n", p1, p2); return 0; } 

et j’ai eu une erreur:

/tmp/ccKKrQ7u.o: Dans la fonction ‘carré’:
main.c :(. text + 0x0): définition multiple de ‘carré’
/tmp/ccwJoxlY.o:ac:(.text+0x0): défini pour la première fois ici
collect2: ld a renvoyé 1 état de sortie

J’ai donc déplacé l’implémentation de la fonction square dans le fichier ac et cela fonctionne.

Quelqu’un sait-il pourquoi?

merci

En règle générale, les fichiers * .c sont compilés en fichiers * .o et les fichiers * .o sont liés pour créer l’exécutable. Les fichiers * .h ne sont pas compilés, ils sont inclus textuellement dans les fichiers * .c, au moment où ils sont # inclus.

Ainsi, en incluant # “ah” deux fois, dans deux fichiers * .c distincts, vous avez placé votre définition de square () dans deux fichiers distincts. Une fois ceux-ci compilés, vous obtenez deux copies de square (), une dans chaque fichier * .o. Ensuite, lorsque vous les liez, l’éditeur de liens voit les deux fichiers et génère une erreur.

Comment éviter cela? Vous avez déjà découvert cela. Ne mettez pas les définitions de fonctions dans les fichiers * .h. Placez-les dans des fichiers * .c et ne placez que les déclarations de fonction dans les fichiers * .h.

Ne mettez pas de code dans vos fichiers d’en-tête. Vos deux fichiers .c incluent ah , ils ont donc tous les deux une implémentation de square , donc votre éditeur de liens se plaint.

Cela est dû au fait que votre compilateur C crée chaque fichier .c dans un fichier object (.o), puis lie les fichiers object pour rendre l’exécutable. Le contenu d’un fichier .c et de tous les fichiers qu’il contient sont appelés unités de compilation .

Votre exemple comporte deux unités de compilation:

  • main.c , y compris stdio.h et ah → comstack en main.o
  • ac , y compris ah → comstack entre autres

L’éditeur de liens (ld) tente ensuite de lier les fichiers .o, mais trouve qu’ils définissent tous deux square() , car il se trouvait dans l’ ah partagé – d’où l’erreur. C’est pourquoi, comme certains l’ont déjà souligné, vous ne devez pas insérer de définitions de fonctions dans les en-têtes.

Si vous avez l’utilitaire nm installé (ce que vous devriez avoir), vous pouvez exécuter

 $ nm main.o $ nm ao 

dans le shell pour voir ce square existe dans les deux fichiers.

(Edit: le terme auquel je pensais était en fait unité de traduction , mais en cherchant, ils semblent signifier à peu près la même chose.)

Quand square() était dans l’en-tête, il était inclus à la fois dans ac et main.c, de sorte que chacun des fichiers object correspondants avait son propre square() , mais sans le modificateur statique, ils avaient exactement le même nom. Le déplacer en ac signifie qu’il n’est défini qu’une seule fois.

Si vous voulez vraiment la fonction dans le fichier d’en-tête, vous pouvez la définir avec inline statique ainsi:

 static inline int square (int x) { return x*x; } 

Statique signifie que chaque fichier .c incluant le fichier .h aura sa propre version de square (), inline signifie que le code sera supprimé en ligne et qu’aucun appel de fonction ne se produira, ce qui est probablement ce que vous voulez ici.

Vous ne pouvez pas écrire une implémentation (corps de la fonction) dans un fichier d’en-tête, sinon l’éditeur de liens trouvera plus d’une référence à cette fonction.

En règle générale, placez uniquement les déclarations dans les fichiers d’en-tête, pas les définitions.

Vous avez répondu à votre propre question: vous avez défini un carré deux fois, une fois dans chaque fichier compilé qui inclut ah

Pour éviter cela, vous pouvez faire de Square une fonction statique

 static int square (int x) { return x*x; } 

et ajoutez l’indice en ligne utilisé par votre compilateur, par exemple inline ou __inline .