Fonctions dans les en-têtes C?

J’ai entendu dire que vous placiez des prototypes de fonctions dans des fichiers d’en-tête, puis que vous définissiez les définitions de fonctions dans un fichier .c. Cependant, quel est ce fichier .c. Par exemple, si vous deviez inclure un fichier “foo.h”, appelez le fichier .c susmentionné “foo.c”, placez-y la définition de la fonction, placez-la au même endroit que foo.h lorsque vous essayez d’inclure foo.h, la fonction sera-t-elle transférée du fichier c et sera-t-elle prête à être utilisée?

Non, le simple fait de placer le .c avec le .h et de l’inclure dans votre code ne modifie pas, par magie, la définition des fonctions.

Vous devez comstackr le foo.c séparément dans un fichier object (dans le cas de Linux, il s’agit d’un fichier .o ). Par exemple, en utilisant la commande –

 gcc -c foo.c -o foo.o 

Maintenant, ce foo.o doit être lié à votre programme actuel. Cela peut être fait simplement en passant le fichier object lors de la compilation en tant que

 gcc test.c foo.o -o test.out 

Si vous ne liez pas le foo.o à votre programme, votre éditeur de liens ne sera pas en mesure de trouver les implémentations pour les fonctions définies dans celui-ci et générera une erreur de l’éditeur de liens comme –

Référence non foo_function à la fonction foo_function .

Les fichiers d’en-tête sont simplement conventionnels. Le préprocesseur C gère les directives #include s et le compilateur voit l’entrée prétraitée (mais vous pouvez copier et coller une énorme quantité de code C pour obtenir l’équivalent de #include ). En fait, le préprocesseur ne se soucie pas de savoir si vous #include "foo.h" ou #include "foo.c" , mais ce dernier a souvent un goût médiocre. Nommer des fichiers d’en-tête avec un suffixe .h n’est qu’une convention (très courante).

Ainsi, si vous avez une définition de fonction dans un en-tête inclus dans plusieurs fichiers source (techniquement, les unités de traduction ), cette définition de fonction figure dans chacune de ces unités de traduction.

Ce qui se passe alors dépend de la définition de cette fonction (elle devrait être static ou encore meilleure static inline ).

En pratique, vous devez limiter les définitions de fonction dans l’en-tête à static ou static inline . Si vous ne déclarez pas une définition de fonction static void foo(int x) { /* something */ } dans un en-tête inclus dans plusieurs fichiers *.c , vous aurez plusieurs définitions d’erreurs foo au moment du lien . Et le principal intérêt de mettre une définition de fonction dans un en-tête est de permettre l’inline (d’où l’indice inline ); sinon (dans le cas habituel), vous n’avez pas besoin de cela et vous venez de placer le prototype de la fonction dans le fichier d’en-tête et la définition de la fonction dans l’ un de vos fichiers *.c .

Si vous avez des fonctions courtes et rapides, il peut être judicieux de les définir en tant static inline (donnez-leur leur corps) dans vos fichiers d’en-tête (mais bien sûr, cela augmente le temps de compilation). Sinon, cela ne vaut pas la charge.

Certains fichiers d’en-tête peuvent avoir de longues macros (de dizaines de lignes physiques, sauf la dernière terminée par une barre oblique inverse) étendues à des définitions de fonctions. Regardez dans sglib pour un exemple.

Notez que inline est aujourd’hui (tout comme register dans la décennie précédente) un indice pour le compilateur (pour l’optimisation du développement en ligne ), qui est autorisé à l’ignorer (également, de nombreux compilateurs optimiseurs sont capables de fonctions inline sans annotation connaître le corps de la fonction appelée).

N’oubliez pas d’activer tous les avertissements et informations de débogage. Avec GCC, utilisez gcc -Wall -Wextra -g , éventuellement avec -Wssortingct-prototypes . Vous pouvez obtenir les fichiers inclus avec -H (et le formulaire prétraité avec -C -E ).

Reportez-vous à un site de référence C et à la norme C11 n1570 pour plus de détails. Lisez le manuel de référence GNU cpp sur le prétraitement.