L'utilité des #include ?
Résolu
Neoflash146
Messages postés
16
Date d'inscription
Statut
Membre
Dernière intervention
-
Neoflash146 Messages postés 16 Date d'inscription Statut Membre Dernière intervention -
Neoflash146 Messages postés 16 Date d'inscription Statut Membre Dernière intervention -
Bonjour,
J'ai commencé à faire des petits programmes utilisant les fonctions de string.h pour me familiariser avec les chaînes de caractères. J'en ai utilisé quelques unes avec printf, tout fonctionnait très bien.
Ensuite, j'ai supprimé #include <string.h> pour voir...Et ça fonctionnait toujours.
Après j'ai supprimé toutes les directives de préprocesseur et le programme fonctionnait toujours parfaitement bien ! Ce n'est pas normal ? Est-ce que parce que j'ai build le programme avant d'enlever les directives les fichiers sont resté inclus ?
Merci de m'expliquer.
J'ai commencé à faire des petits programmes utilisant les fonctions de string.h pour me familiariser avec les chaînes de caractères. J'en ai utilisé quelques unes avec printf, tout fonctionnait très bien.
Ensuite, j'ai supprimé #include <string.h> pour voir...Et ça fonctionnait toujours.
Après j'ai supprimé toutes les directives de préprocesseur et le programme fonctionnait toujours parfaitement bien ! Ce n'est pas normal ? Est-ce que parce que j'ai build le programme avant d'enlever les directives les fichiers sont resté inclus ?
Merci de m'expliquer.
A voir également:
- L'utilité des #include ?
- Glary utilite - Télécharger - Nettoyage
- Java utilité ✓ - Forum Java
- Utilité tableau croisé dynamique - Guide
- Google drive utilité - Guide
- Processeur utilité - Guide
4 réponses
Pour faire simple, une fonction en C, pour être utilisable, doit normalement être au préalable déclarée, ce qui signifie que l'on connaît son prototype. Même si ton compilateur peut tolérer des déclarations implicites, c'est clairement déconseillé car ça conduit à des programmes illisibles et aux dépendances incompréhensibles. On va donc supposer que ne pas respecter cette règle est une erreur de programmation.
À quoi sert #include ?
Est-ce qu'un #include est pour autant nécessaire ? Considérons ce programme :
On pourrait tout à fait écrire ceci, ça compile sans aucun warning :
... car finalement, du fichier stdio.h, seule la ligne en italique que j'ai reportée nous intéresse. Pour autant, le code de la fonction printf n'est pas reporté dans mon fichier ".c". Seul le prototype est intéressant à ce stade, car l'implémentation de la fonction "printf" sera donc "retrouvée" au moment de linker. Cet exemple semble montrer que #include n'est finalement pas si indispensable et a plutôt un intérêt d'ordre pratique.
En effet, si on travaille dans un seul fichier, on pourrait copier coller les prototypes de toutes les fonctions dont on a besoin en début de ce fichier et ça marcherait très bien, du moment qu'on récupère l'implémentation de ces fonctions à la fin de la compilation (plus précisément au linkage). Et c'est finalement ce que j'ai fait dans l'exemple ci-dessus.
Quel est l'intérêt de #include ?
Mais déjà à ce stade on comprend que ça ne serait clairement pas pratique. Supposons qu'on travaille sur plusieurs fichiers, cela signifierait qu'en plus on devrait faire ces copier-coller potentiellement dans chacun de ces fichiers ! Je passe sur les considérations de définitions multiples pour ne pas compliquer le propos, mais ce serait également un point qui justifie l'intérêt des #include et des headers.
Supposons qu'on développe la libc. On a codé des fonctions relatives aux strings, d'autres aux entrées sorties etc. On a envie de les mettre facilement à disposition des gens qui vont développer en utilisant la libc. On va donc rassembler ces fonctions par "thèmes", par exemple les fonctions relatives aux chaînes de caractères dans un même header (string.h). Dans l'absolu, ce développeur utilisant ces fonctions aura besoin de "copier coller" leur déclaration. Il pourra le faire facilement en important ce header.
Comment savoir quels #include faire ?
On perçoit donc qu'il suffit qu'un fichier .c inclue tous les .h permettant de rassembler les déclarations de fonctions implémentées à l'extérieur de ce .h pour résoudre la plupart des problèmes. Donc par exemple si j'utilise la fonction "strdup", déclarée dans <string.h>, il suffit que j'inclue <string.h>. On peut imaginer que ce même programme inclue un autre header, mettons "toto.h" qui lui même inclue <string.h>. Mais comme finalement l'implémentation de "toto.h" peut potentiellement évoluer, en toute rigueur, je dois explicitement inclure <string.h> et "toto.h".
La seule question au final, c'est dans quel header une fonction est déclarée. Si tu es sous linux tu peux facilement retrouver cette information avec le man avec la commande suivante :
http://fr.wikipedia.org/wiki/Man_(Unix)
Note que tu peux aussi taper ça dans google (en particulier si tu es sous windows), tu tomberas par exemple sur :
http://manpagesfr.free.fr/man/man3/printf.3.html
On voit donc ici que printf est fourni par <stdio.h>
Comment marche et que fait #include ?
Attachons-nous maintenant à la manière dont c'est réalisé en C. Ce "copier-coller", s'il devait avoir lieu, se ferait au moment où le développeur écrit son code, donc avant la compilation. On sent donc que ce traitement doit être effectué avant la compilation. C'est exactement ce qui est fait en pratique. Toutes les lignes qui commencent par un # sont traitées par le pré-compilateur. En outre, toutes les lignes effectuant un #include déclenche un copier coller.
https://forums.commentcamarche.net/forum/affich-37604426-la-compilation-et-les-modules-en-c-et-en-c#les-phases-de-compilation
Ensuite la compilation a proprement parler peut avoir lieu. En général on écrit des fichiers C en général en vue de produire à terme un exécutable ou une librairie (que je vais appeler binaire). Chaque fichier ".c" produit un fichier ".o", et l'ensemble de ces fichiers ".o" et les librairies tierces sont "rassemblées" pour produire le binaire final (étape de linkage). Si toutes les déclarations implicites de fonctions sont levées au linkage, le binaire compile après quelque warning. Si au contraire un symbole (par exemple une déclaration de fonction) n'est pas trouvé (ou en plusieurs examplaires) alors l'étape de linkage échoue. C'est l'un des intérêts supplémentaires des #include headers : ceux-ci sont généralement conçus pour qu'il n'y ait pas de risque de définitions multiples à l'aide de verrous (voir #ifndef, #define, #endif) au moment du linkage.
Bonne chance
À quoi sert #include ?
Est-ce qu'un #include est pour autant nécessaire ? Considérons ce programme :
#include <stdio.h> int main() { printf("coucou"); return 0; }
On pourrait tout à fait écrire ceci, ça compile sans aucun warning :
extern int printf (const char *__restrict __format, ...); int main() { printf("coucou"); return 0; }
... car finalement, du fichier stdio.h, seule la ligne en italique que j'ai reportée nous intéresse. Pour autant, le code de la fonction printf n'est pas reporté dans mon fichier ".c". Seul le prototype est intéressant à ce stade, car l'implémentation de la fonction "printf" sera donc "retrouvée" au moment de linker. Cet exemple semble montrer que #include n'est finalement pas si indispensable et a plutôt un intérêt d'ordre pratique.
En effet, si on travaille dans un seul fichier, on pourrait copier coller les prototypes de toutes les fonctions dont on a besoin en début de ce fichier et ça marcherait très bien, du moment qu'on récupère l'implémentation de ces fonctions à la fin de la compilation (plus précisément au linkage). Et c'est finalement ce que j'ai fait dans l'exemple ci-dessus.
Quel est l'intérêt de #include ?
Mais déjà à ce stade on comprend que ça ne serait clairement pas pratique. Supposons qu'on travaille sur plusieurs fichiers, cela signifierait qu'en plus on devrait faire ces copier-coller potentiellement dans chacun de ces fichiers ! Je passe sur les considérations de définitions multiples pour ne pas compliquer le propos, mais ce serait également un point qui justifie l'intérêt des #include et des headers.
Supposons qu'on développe la libc. On a codé des fonctions relatives aux strings, d'autres aux entrées sorties etc. On a envie de les mettre facilement à disposition des gens qui vont développer en utilisant la libc. On va donc rassembler ces fonctions par "thèmes", par exemple les fonctions relatives aux chaînes de caractères dans un même header (string.h). Dans l'absolu, ce développeur utilisant ces fonctions aura besoin de "copier coller" leur déclaration. Il pourra le faire facilement en important ce header.
Comment savoir quels #include faire ?
On perçoit donc qu'il suffit qu'un fichier .c inclue tous les .h permettant de rassembler les déclarations de fonctions implémentées à l'extérieur de ce .h pour résoudre la plupart des problèmes. Donc par exemple si j'utilise la fonction "strdup", déclarée dans <string.h>, il suffit que j'inclue <string.h>. On peut imaginer que ce même programme inclue un autre header, mettons "toto.h" qui lui même inclue <string.h>. Mais comme finalement l'implémentation de "toto.h" peut potentiellement évoluer, en toute rigueur, je dois explicitement inclure <string.h> et "toto.h".
La seule question au final, c'est dans quel header une fonction est déclarée. Si tu es sous linux tu peux facilement retrouver cette information avec le man avec la commande suivante :
http://fr.wikipedia.org/wiki/Man_(Unix)
man 3 printf
Note que tu peux aussi taper ça dans google (en particulier si tu es sous windows), tu tomberas par exemple sur :
http://manpagesfr.free.fr/man/man3/printf.3.html
On voit donc ici que printf est fourni par <stdio.h>
Comment marche et que fait #include ?
Attachons-nous maintenant à la manière dont c'est réalisé en C. Ce "copier-coller", s'il devait avoir lieu, se ferait au moment où le développeur écrit son code, donc avant la compilation. On sent donc que ce traitement doit être effectué avant la compilation. C'est exactement ce qui est fait en pratique. Toutes les lignes qui commencent par un # sont traitées par le pré-compilateur. En outre, toutes les lignes effectuant un #include déclenche un copier coller.
https://forums.commentcamarche.net/forum/affich-37604426-la-compilation-et-les-modules-en-c-et-en-c#les-phases-de-compilation
Ensuite la compilation a proprement parler peut avoir lieu. En général on écrit des fichiers C en général en vue de produire à terme un exécutable ou une librairie (que je vais appeler binaire). Chaque fichier ".c" produit un fichier ".o", et l'ensemble de ces fichiers ".o" et les librairies tierces sont "rassemblées" pour produire le binaire final (étape de linkage). Si toutes les déclarations implicites de fonctions sont levées au linkage, le binaire compile après quelque warning. Si au contraire un symbole (par exemple une déclaration de fonction) n'est pas trouvé (ou en plusieurs examplaires) alors l'étape de linkage échoue. C'est l'un des intérêts supplémentaires des #include headers : ceux-ci sont généralement conçus pour qu'il n'y ait pas de risque de définitions multiples à l'aide de verrous (voir #ifndef, #define, #endif) au moment du linkage.
Bonne chance
Bonjour,
Ce n'est pas anormal puisque le code des fonctions de string.h sont contenues dans la bibliothèque standard, linkée automatiquement lors de la compilation. Alors à quoi sert string.h ? Elle contient, entres autres, les prototypes des fonctions *str*. Du coup, cela permet de meilleurs contrôles lors de la compilation. Si tu ne le mets pas, cela doit te générer un avertissement (sauf si ton compilateur est trop permissif...). Bref, il faut inclure le header si tu utilises ses fonctions.
Google is your friend
Ce n'est pas anormal puisque le code des fonctions de string.h sont contenues dans la bibliothèque standard, linkée automatiquement lors de la compilation. Alors à quoi sert string.h ? Elle contient, entres autres, les prototypes des fonctions *str*. Du coup, cela permet de meilleurs contrôles lors de la compilation. Si tu ne le mets pas, cela doit te générer un avertissement (sauf si ton compilateur est trop permissif...). Bref, il faut inclure le header si tu utilises ses fonctions.
Google is your friend
C'est ça que je ne comprend pas, pourquoi les fonctions (comme strstr et printf) fonctionnaient toujours même quand je n'incluais aucun header ?
C'est pas la conclusion qu'il faut lire, mais ce qu'il y a au dessus.
Alors, je remets ici le passage qui répond à ta question.
Ce n'est pas anormal puisque le code des fonctions de string.h sont contenues dans la bibliothèque standard, linkée automatiquement lors de la compilation. Alors à quoi sert string.h ? Elle contient, entres autres, les prototypes des fonctions *str*. Du coup, cela permet de meilleurs contrôles lors de la compilation. Si tu ne le mets pas, cela doit te générer un avertissement (sauf si ton compilateur est trop permissif...).
Donc, si tu n'as pas d'avertissements, cela veut dire que ton compilateur est trop permissif, mal configuré...
Par exemple, as-tu -Wall comme flag ?
Alors, je remets ici le passage qui répond à ta question.
Ce n'est pas anormal puisque le code des fonctions de string.h sont contenues dans la bibliothèque standard, linkée automatiquement lors de la compilation. Alors à quoi sert string.h ? Elle contient, entres autres, les prototypes des fonctions *str*. Du coup, cela permet de meilleurs contrôles lors de la compilation. Si tu ne le mets pas, cela doit te générer un avertissement (sauf si ton compilateur est trop permissif...).
Donc, si tu n'as pas d'avertissements, cela veut dire que ton compilateur est trop permissif, mal configuré...
Par exemple, as-tu -Wall comme flag ?
Ce sont les options lancées au moment de la compilation.
Par défaut, le compilateur est trop permissif (il ne respecte même pas, me semble-t-il, stricto sensu, le standard par défaut). Il faut donc aller dans les options de Code::Blocks pour paramétrer correctement le compilateur et lui faire afficher les avertissements.
Par défaut, le compilateur est trop permissif (il ne respecte même pas, me semble-t-il, stricto sensu, le standard par défaut). Il faut donc aller dans les options de Code::Blocks pour paramétrer correctement le compilateur et lui faire afficher les avertissements.
Je remercie aussi fiddy qui m'a dit que je devais mettre Wall dans les flag. D'ailleurs au début je croyais que c'était "mur" alors que c'était "Warning all", juste pour le dire.
http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
Chacune de ces options permet de désactiver des messages de warnings. Le mot clé "all" permet de toutes les activer d'un coup.
Bonne chance
Chacune de ces options permet de désactiver des messages de warnings. Le mot clé "all" permet de toutes les activer d'un coup.
-Wall est loin de tous les activer.
Par exemple, pour bien faire, il faudrait rajouter -Wextra, -Wunreachable-code, -Wunreachable-code et j'en passe des dizaines...