L'utilité des #include ?

Résolu/Fermé
Neoflash146 Messages postés 16 Date d'inscription vendredi 28 septembre 2012 Statut Membre Dernière intervention 26 janvier 2014 - 24 août 2013 à 16:11
Neoflash146 Messages postés 16 Date d'inscription vendredi 28 septembre 2012 Statut Membre Dernière intervention 26 janvier 2014 - 26 août 2013 à 10:32
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.

A voir également:

4 réponses

mamiemando Messages postés 33077 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 18 avril 2024 7 749
25 août 2013 à 00:44
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 :

#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
3
fiddy Messages postés 11069 Date d'inscription samedi 5 mai 2007 Statut Contributeur Dernière intervention 23 avril 2022 1 835
25 août 2013 à 00:49
Belle dissertation ;-)
0
KX Messages postés 16733 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 31 janvier 2024 3 015
25 août 2013 à 00:51
c'est mamiemando ;-)
0
Neoflash146 Messages postés 16 Date d'inscription vendredi 28 septembre 2012 Statut Membre Dernière intervention 26 janvier 2014
25 août 2013 à 12:02
Très honnêtement tu ne m'as pas appris grand-chose vu que sur le SdZ je viens justement de finir le chapitre sur les directives de préprocesseur pour passer aux structures, mais comme tu l'as expliqué différemment de Mateo21, je crois avoir compris :)
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.
0
mamiemando Messages postés 33077 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 18 avril 2024 7 749
25 août 2013 à 22:47
Dans gcc, le flag -W (en français option de compilation) peut être effectivement suivi de nombreuses chaînes.
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
0
fiddy Messages postés 11069 Date d'inscription samedi 5 mai 2007 Statut Contributeur Dernière intervention 23 avril 2022 1 835
25 août 2013 à 22:57
Je me permets de nuancer le commentaire de mamiemando
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...
0
tj anh Messages postés 18 Date d'inscription dimanche 11 août 2013 Statut Membre Dernière intervention 24 août 2013
24 août 2013 à 16:37
quelles foncs ???
je pense vous avez utilisez les foncs qui ne sont pas de string.h
0
fiddy Messages postés 11069 Date d'inscription samedi 5 mai 2007 Statut Contributeur Dernière intervention 23 avril 2022 1 835
Modifié par fiddy le 24/08/2013 à 16:46
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
0
Neoflash146 Messages postés 16 Date d'inscription vendredi 28 septembre 2012 Statut Membre Dernière intervention 26 janvier 2014
24 août 2013 à 16:49
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 ?
0
fiddy Messages postés 11069 Date d'inscription samedi 5 mai 2007 Statut Contributeur Dernière intervention 23 avril 2022 1 835
Modifié par fiddy le 24/08/2013 à 16:58
Je viens de te le dire...
Relis mon post.
0
Neoflash146 Messages postés 16 Date d'inscription vendredi 28 septembre 2012 Statut Membre Dernière intervention 26 janvier 2014
24 août 2013 à 17:28
"Bref, il faut inclure le header si tu utilises ses fonctions."
Je n'inclus pas le header.
J'utilise ses fonctions.
Le compilateur ne me met pas d'avertissement (j'utilise GNU GCC Compiler avec Code::Blocks)
Le programme fonctionne.
Quid ?
0
fiddy Messages postés 11069 Date d'inscription samedi 5 mai 2007 Statut Contributeur Dernière intervention 23 avril 2022 1 835
24 août 2013 à 17:49
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 ?
0
Neoflash146 Messages postés 16 Date d'inscription vendredi 28 septembre 2012 Statut Membre Dernière intervention 26 janvier 2014
24 août 2013 à 19:17
Heu...Je sais pas...C'est quoi un flag ?
0
fiddy Messages postés 11069 Date d'inscription samedi 5 mai 2007 Statut Contributeur Dernière intervention 23 avril 2022 1 835
Modifié par fiddy le 25/08/2013 à 00:16
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.
0