En C, si vous définissez une fonction dans un fichier d'en-tête, cette fonction apparaîtra dans chaque module compilé qui inclut ce fichier d'en-tête et un symbole public sera exporté pour la fonction. Donc, si la fonction additup est définie dans header.h, et foo.c et bar.c incluent tous les deux header.h, alors foo.o et bar.o incluront tous les deux des copies d'additup.
Lorsque vous allez lier ces deux fichiers objets ensemble, l'éditeur de liens verra que le symbole additup est défini plusieurs fois et ne le permettra pas.
Si vous déclarez que la fonction est statique, aucun symbole ne sera exporté. Les fichiers objets foo.o et bar.o contiendront tous les deux des copies distinctes du code de la fonction, et ils pourront les utiliser, mais l'éditeur de liens ne pourra voir aucune copie de la fonction, donc il ne se plaindra pas. Bien entendu, aucun autre module ne pourra non plus voir la fonction. Et votre programme sera gonflé de deux copies identiques de la même fonction.
Si vous déclarez uniquement la fonction dans le fichier d'en-tête, mais ne la définissez pas, puis la définissez dans un seul module, l'éditeur de liens verra une copie de la fonction, et chaque module de votre programme pourra la voir et utilise le. Et votre programme compilé ne contiendra qu'une seule copie de la fonction.
Ainsi, vous pouvez avoir la définition de la fonction dans le fichier d'en-tête en C, c'est juste un mauvais style, une mauvaise forme et une mauvaise idée générale.
(Par «déclarer», je veux dire fournir un prototype de fonction sans corps; par «définir», je veux dire fournir le code réel du corps de fonction; c'est la terminologie C standard.)
#ifndef HEADER_H
n'est-il pas censé empêcher?C et C ++ se comportent à peu près de la même manière à cet égard - vous pouvez avoir des
inline
fonctions dans les en-têtes. En C ++, toute méthode dont le corps est à l'intérieur de la définition de classe l'est implicitementinline
. Si vous souhaitez faire de même en C, déclarez les fonctionsstatic inline
.la source
static inline
" ... et vous aurez toujours plusieurs copies de la fonction dans chaque unité de traduction qui l'utilise. En C ++ avec nonstatic
inline
fonction, vous n'aurez qu'une seule copie. Pour avoir réellement l'implémentation dans l'en-tête en C, vous devez 1) marquer l'implémentation commeinline
(par exempleinline void func(){do_something();}
), et 2) dire en fait que cette fonction sera dans une unité de traduction particulière (par exemplevoid func();
).Le concept d'un fichier d'en-tête a besoin d'une petite explication:
Soit vous donnez un fichier sur la ligne de commande du compilateur, soit vous faites un '#include'. La plupart des compilateurs acceptent un fichier de commandes avec l'extension c, C, cpp, c ++, etc. comme fichier source. Cependant, ils incluent généralement une option de ligne de commande pour permettre également l'utilisation de toute extension arbitraire dans un fichier source.
Généralement, le fichier donné sur la ligne de commande est appelé «Source», et celui inclus est appelé «En-tête».
L'étape du préprocesseur les prend tous et fait tout apparaître comme un gros fichier unique au compilateur. Ce qui était dans l'en-tête ou dans la source n'est en fait pas pertinent à ce stade. Il y a généralement une option d'un compilateur qui peut afficher la sortie de cette étape.
Donc, pour chaque fichier qui a été donné sur la ligne de commande du compilateur, un énorme fichier est donné au compilateur. Cela peut avoir du code / des données qui occuperont de la mémoire et / ou créeront un symbole à référencer à partir d'autres fichiers. Maintenant, chacun d'eux va générer une image «objet». L'éditeur de liens peut donner un «symbole en double» si le même symbole se trouve dans plus de deux fichiers objets qui sont liés ensemble. C'est peut-être la raison; il n'est pas conseillé de mettre du code dans un fichier d'en-tête, ce qui peut créer des symboles dans le fichier objet.
Les 'inline' sont généralement en ligne .. mais lors du débogage, ils peuvent ne pas être en ligne. Alors pourquoi l'éditeur de liens ne donne-t-il pas d'erreurs définies de façon multiple? Simple ... Ce sont des symboles «faibles», et tant que toutes les données / code pour un symbole faible de tous les objets ont la même taille et le même contenu, le lien gardera une copie et supprimera la copie des autres objets. Ça marche.
la source
Vous pouvez le faire en C99: les
inline
fonctions sont assurées d'être fournies ailleurs, donc si une fonction n'a pas été insérée, sa définition est traduite en une déclaration (c'est-à-dire que l'implémentation est rejetée). Et, bien sûr, vous pouvez utiliserstatic
.la source
Citations standard C ++
Le projet de norme C ++ 17 N4659 10.1.6 "Le spécificateur en ligne" dit que les méthodes sont implicitement en ligne:
et puis plus bas, nous voyons que les méthodes en ligne peuvent non seulement peuvent, mais doivent être définies sur toutes les unités de traduction:
Ceci est également explicitement mentionné dans une note au 12.2.1 "Fonctions membres":
Implémentation de GCC 8.3
main.cpp
Compilez et visualisez les symboles:
production:
on voit alors
man nm
que leMyClass::myMethod
symbole est marqué comme faible sur les fichiers objets ELF, ce qui implique qu'il peut apparaître sur plusieurs fichiers objets:la source
Probablement pour la même raison que vous devez placer l'implémentation complète de la méthode dans la définition de classe en Java.
Ils peuvent ressembler, avec des crochets ondulés et plusieurs des mêmes mots clés, mais ce sont des langues différentes.
la source