2010-10-28 22 views

Répondre

8

Il y a quelques cas d'utilisation pour cela. Le plus évident est que votre programme principal a rarement besoin d'un fichier d'en-tête.

La seconde est où ont pas un en-tête pour chaque fichier C. Je l'ai mis en place des bibliothèques avant (disons une bibliothèque BTree aux fins de cette réponse) où chaque fonction est dans son propre fichier source, mais il y a un fichier d'en-tête bibliothèque à l'échelle, quelque chose comme:

btree.h 
btree_priv.h 
btreeInit.c 
btreeDestroy.c 
btreeConfig.c 

et bientôt. Le fichier d'en-tête privé est destiné à des éléments qui doivent être partagés entre le code mais qui ne doivent pas être publiés dans l'API.

+0

Donc, je suppose que le corollaire de cette réponse est que le modèle de façade mène souvent à une relation non-un-à-un .c à .h? – Tommy

5

plus évidemment lorsque le .c est entièrement autonome et n'a pas besoin d'avoir des prototypes ou externes pour d'autres fichiers .c. ceci est essentiellement pour les très petits programmes, ou ceux qui exportent via un fichier def pour se connecter à une interface prédéfinie.

1

Dans le cas où vous n'auriez pas besoin des déclarations du fichier .h, mais ce n'est effectivement jamais le cas.

3

Lorsque le fichier .c contient des données qui n'ont pas besoin d'être utilisées par un code.

Certes, c'est rare - mais cela peut arriver. Par exemple, je l'ai fait dans les dispositifs intégrés, pour peupler le tampon de trame vidéo avec des graphiques de démarrage.

3

Si vous divisez votre programme en un certain nombre de modules, il y aura généralement un module "principal", qui contient la fonction main() et peut-être d'autres choses. Si ce module ne contient aucun élément supposé être appelé ou utilisé par un autre module, vous n'avez pas besoin d'exporter une interface dans un fichier .h.

5

J'ai vu des bases de code massives où un seul fichier .h définit l'interface de certains composants, et plusieurs fichiers .c l'implémentent. La mise en œuvre a été divisée en plusieurs fichiers uniquement pour des raisons de maintenabilité, et a été tentée à certaines limites logiques. On pourrait argumenter qu'une telle limite logique pourrait être appliquée pour diviser le composant en sous-composants et par conséquent avoir plusieurs fichiers d'en-tête, mais les décisions de conception sont rarement une affaire blanche, et parfois cette approche a du sens.

1

Souvent, je crée un fichier d'en-tête pour 'main' même si je ne m'attends pas à ce que d'autres codes y accèdent, car souvent (1) une version de débogage nécessitera quelque chose en dehors du module principal pour accéder à quelque chose dedans, ou (2) le module principal finira par devoir être divisé en raison des limitations de compilateur (système embarqué). Le fait que chaque fichier .c inclue son propre fichier .h est suffisamment puissant pour que je crée souvent des fichiers .h presque vides, même pour les fichiers .c qui définissent des éléments qui ne sont pas référencés dans le code (comme les tables de saut d'interruption) , etc.).

Les conventions de dénomination deviennent un peu plus délicates lorsqu'un fichier comprend plusieurs fichiers générés par le programme ou comprend des fichiers qui doivent être compilés plusieurs fois (par ex.un de mes projets a deux moteurs, dont le code est identique sauf qu'ils utilisent des ports d'E/S différents et des variables différentes; mon fichier motor.c contient:

 
#define LOCK L0 
#include "motor.i" 
#undef LOCK 
#define LOCK L1 
#include "motor.i" 
#under LOCK 

Notez que sur ce point particulier compilateur intégré, l'opérateur -> est très inefficace, donc une déclaration comme:

 
    L0.speed++; 

compilera à une des instructions, alors qu'un déclaration comme:

 
    L0->speed++; 

se traduira en cinq instructions si la « vitesse » est le premier élément de la structure, ou sept si elle occupe une autre position. Il est donc beaucoup plus rapide et plus économe en espace de dupliquer du code avec des adresses résolvables constantes plutôt que d'avoir une routine qui gère les deux moteurs.

S'il y a un fichier supplémentaire associé à un fichier .c, et qu'il contient du code réel, je le nommerai ".i". Je ne sais pas quoi faire s'il y en a plus d'un.

3

Je pense que l'attente d'un mappage 1-to-1 entre les fichiers .c et .h est une mauvaise hypothèse de départ. Jetez cela et commencez frais. : D

En regardant votre question différemment, vous pourriez demander "Quand est-il approprié de créer un fichier d'en-tête?"

Je suggère ce qui suit, le terme « module de code » est un groupe (généralement un répertoire) d'un ou plusieurs fichiers liés .c:

  1. Créer un en-tête pour les interfaces publiques/définitions doit être disponible pour les autres modules de code.
  2. Créez un en-tête pour les interfaces/définitions privées qui doivent être partagées dans un module de code mais non partagées avec d'autres modules de code.

Ce sont les seuls fichiers d'en-tête dont vous avez besoin. Si aucun de ces éléments n'est requis, vous n'avez pas besoin d'en-tête.

Certains codeurs aiment séparer artificiellement prototypes/macros/typedefs/etc dans un .h distinct des globals/fonctions dans leur .c. Je recommande contre cette approche et suggère d'avoir toutes les fonctionnalités connexes dans un fichier. Ensuite, déplacez vers les en-têtes si nécessaire les éléments qui sont absolument nécessaires pour empêcher extern dans d'autres fichiers.