web-dev-qa-db-fra.com

Où mettre les déclarations, l'en-tête ou la source?

Dois-je mettre les inclusions dans le fichier d'en-tête ou le fichier source? Si le fichier d'en-tête contient les instructions include, alors si j'inclus ce fichier d'en-tête dans ma source, mon fichier source aura-t-il tous les fichiers inclus qui étaient dans mon en-tête? Ou dois-je simplement les inclure dans mon fichier source uniquement?

98
Mohit Deshpande

Ne mettez les inclus dans un en-tête que si l'en-tête en a besoin.

Exemples:

  • Votre fonction renvoie le type size_t. Ensuite #include <stddef.h> dans le fichier en-tête.
  • Votre fonction utilise strlen. Ensuite #include <string.h> dans le fichier source.
123
schot

Il y a eu pas mal de désaccords à ce sujet au fil des ans. À une époque, il était traditionnel qu'un en-tête seulement déclare ce qui était dans le module auquel il était lié, donc beaucoup en-têtes avaient des exigences spécifiques que vous #include un certain ensemble d'en-têtes (dans un ordre spécifique). Certains programmeurs C extrêmement traditionnels suivent toujours ce modèle (religieusement, dans au moins certains cas).

Plus récemment, la tendance est de rendre la plupart des en-têtes autonomes. Si cet en-tête requiert autre chose, l'en-tête lui-même gère cela, en s'assurant que tout ce dont il a besoin est inclus (dans le bon ordre, s'il y a des problèmes de commande). Personnellement, je préfère cela - en particulier lorsque l'ordre des en-têtes peut être important, il résout le problème une fois, au lieu d'exiger que tous ceux qui l'utilisent résolvent à nouveau le problème.

Notez que la plupart des en-têtes ne doivent contenir que des déclarations. Cela signifie que l'ajout d'un en-tête inutile ne devrait (normalement) avoir aucun effet sur votre exécutable final. Le pire qui arrive, c'est que cela ralentit un peu la compilation.

26
Jerry Coffin

Votre #includes doivent être des fichiers d'en-tête, et chaque fichier (source ou en-tête) doit #include les fichiers d'en-tête dont il a besoin. Les fichiers d'en-tête doivent #include les fichiers d'en-tête minimum nécessaires, et les fichiers source devraient également, bien que ce ne soit pas aussi important pour les fichiers source.

Le fichier source aura les en-têtes qu'il _ #includes, et les en-têtes qu'ils #include, et ainsi de suite jusqu'à la profondeur d'imbrication maximale. C'est pourquoi vous ne voulez pas de superflu #includes dans les fichiers d'en-tête: ils peuvent faire en sorte qu'un fichier source inclue un grand nombre de fichiers d'en-tête dont il n'a peut-être pas besoin, ce qui ralentit la compilation.

Cela signifie qu'il est tout à fait possible que les fichiers d'en-tête soient inclus deux fois, ce qui peut poser problème. La méthode traditionnelle consiste à mettre "include guard" dans les fichiers d'en-tête, comme celui-ci pour le fichier foo.h:

#ifndef INCLUDE_FOO_H
#define INCLUDE_FOO_H
/* everything in header goes here */
#endif
12
David Thornley

L'approche que j'ai évoluée en plus de vingt ans est la suivante;

Considérez une bibliothèque.

Il existe plusieurs fichiers C, un fichier H interne et un fichier H externe. Les fichiers C incluent le fichier H interne. Le fichier H interne comprend le fichier H externe.

Vous voyez que du point de vue des compilateurs, comme il compile un fichier C, il y a une hiérarchie;

externe -> interne -> code C

C'est le bon ordre, car ce qui est externe est tout ce dont un tiers a besoin pour utiliser la bibliothèque. Ce qui est interne est nécessaire pour compiler le code C.

6
user82238

Dans certains environnements, la compilation sera plus rapide si l'on n'inclut que les fichiers d'en-tête dont on a besoin. Dans d'autres environnements, la compilation sera optimisée si tous les fichiers source peuvent utiliser la même collection principale d'en-têtes (certains fichiers peuvent avoir des en-têtes supplémentaires au-delà du sous-ensemble commun). Idéalement, les en-têtes doivent être construits afin que plusieurs opérations #include n'aient aucun effet. Il peut être judicieux d'entourer les instructions #include de vérifications de l'inclusion de garde du fichier à inclure, bien que cela crée une dépendance au format de cette garde. De plus, selon le comportement de mise en cache des fichiers d'un système, un #include inutile dont la cible finit par être complètement # ifdef'ed éloigné peut ne pas prendre longtemps.

Une autre chose à considérer est que si une fonction prend un pointeur sur une structure, on peut écrire le prototype comme

 void foo (struct BAR_s * bar); 

sans qu'une définition des BAR_ soit nécessaire. Une approche très pratique pour éviter les inclusions inutiles.

PS - dans beaucoup de mes projets, il y aura un fichier que chaque module devrait inclure, contenant des choses comme des typedefs pour les tailles entières et quelques structures et unions communes [par exemple.

 typedef union {
 unsigned long l; 
 unsigned short lw [2]; 
 unsigned char lb [4]; 
} U_QUAD; 

(Oui, je sais que je serais en difficulté si je passais à une architecture big-endian, mais comme mon compilateur n'autorise pas les structures anonymes dans les unions, l'utilisation d'identifiants nommés pour les octets au sein de l'union nécessiterait leur accès en tant que theUnion.b.b1 etc. qui semble plutôt ennuyeux.

4
supercat

Si le fichier d'en-tête A #includes les fichiers d'en-tête B et C, puis tous les fichiers source qui #includes A recevra également B et C #included. Le pré-processeur effectue littéralement une substitution de texte: partout où il trouve du texte qui dit #include <foo.h> il le remplace par le texte de foo.h fichier.

Il existe différentes opinions sur l'opportunité de mettre #includes dans les en-têtes ou les fichiers source. Personnellement, je préfère mettre tout #includes dans le fichier source par défaut, mais tous les fichiers d'en-tête qui ne peuvent pas être compilés sans d'autres en-têtes prérequis devraient #include ces en-têtes eux-mêmes.

Et chaque fichier d'en-tête doit contenir un garde d'inclusion pour éviter qu'il ne soit inclus plusieurs fois.

4
Vicky

Créez tous vos fichiers afin qu'ils puissent être créés en utilisant uniquement ce qu'ils contiennent. Si vous n'avez pas besoin d'une inclusion dans votre en-tête, supprimez-la. Dans un grand projet, si vous ne maintenez pas cette discipline, vous vous laissez ouvert à casser une build entière lorsque quelqu'un supprime une inclusion d'un fichier d'en-tête qui est utilisé par un consommateur de ce fichier et pas même par l'en-tête.

3
rerun

Vous ne devez inclure dans votre en-tête que les fichiers dont vous avez besoin pour déclarer des constantes et des déclarations de fonction. Techniquement, ces inclus seront également inclus dans votre fichier source, mais pour plus de clarté, vous ne devez inclure dans chaque fichier que les fichiers que vous devez réellement utiliser. Vous devez également les protéger dans votre en-tête contre les inclusions multiples:

#ifndef NAME_OF_HEADER_H
#define NAME_OF_HEADER_H

...definition of header file...

#endif

Cela empêche l'en-tête d'être inclus plusieurs fois, ce qui entraîne une erreur de compilation.

1
JRam930

Votre fichier source aura les instructions include si vous le mettez dans l'en-tête. Cependant, dans certains cas, il serait préférable de les mettre dans le fichier source.

N'oubliez pas que si vous incluez cet en-tête dans d'autres sources, ils obtiendront également les inclusions de l'en-tête, ce qui n'est pas toujours souhaitable. Vous ne devez inclure les éléments que là où ils sont utilisés.

1
Splashdust