Maison  >  Article  >  développement back-end  >  Quelle est la relation entre les fichiers h et les fichiers c ?

Quelle est la relation entre les fichiers h et les fichiers c ?

醉折花枝作酒筹
醉折花枝作酒筹original
2021-06-28 10:45:1014729parcourir

Les fichiers h sont souvent placés en tête des fichiers .c (.cpp), ils sont donc nommés « fichiers d'en-tête ». Le fichier h est un fichier qui extrait les instructions de déclaration répétées du fichier .c, les place dans un nouveau fichier, puis tape une instruction telle que « #include XXXX » dans le fichier .c (.cpp) requis.

Quelle est la relation entre les fichiers h et les fichiers c ?L'environnement d'exploitation de ce tutoriel : système Windows 7, version C++17, ordinateur Dell G3.

Explication détaillée de la relation entre les fichiers .h et .c dans les projets en langage C

À l'époque où le compilateur ne reconnaissait que les fichiers .c (.cpp)) mais ne savait pas quoi .h l'était. À cette époque, les gens écrivaient beaucoup de fichiers .c (.cpp). Peu à peu, les gens ont découvert que les déclarations de nombreux fichiers .c (.cpp) étaient les mêmes, mais ils devaient les répéter mot par mot. mot. Tapez ce contenu dans chaque fichier .c (.cpp). Mais ce qui est encore plus effrayant, c'est que lorsqu'une des déclarations est modifiée, tous les fichiers .c (.cpp) doivent être vérifiés.

Ainsi, les gens extraient les parties répétées, les mettent dans un nouveau fichier, puis tapent des instructions comme #include XXXX dans le fichier .c (.cpp) requis. De cette façon, même si une certaine déclaration change, il n’est pas nécessaire de la rechercher et de la modifier partout. Ce nouveau fichier étant souvent placé en tête du fichier .c (.cpp), il est nommé « fichier d'en-tête » et son extension est .h.

Dans la phase initiale de l'apprentissage de notre langue, notre programme n'a souvent qu'un seul fichier .c ou quelques-uns d'entre eux. À l'heure actuelle, nous rencontrons rarement le casse-tête de l'organisation des fichiers d'en-tête avec l'augmentation de notre programme. , le volume de code a atteint des milliers voire des dizaines de milliers de lignes, et le nombre de fichiers augmente également. A cette époque, l'organisation de ces fichiers devient un problème. En fait, pour parler franchement, l'organisation de ces fichiers est théoriquement un problème de conception de modules en génie logiciel, etc.

Une brève description du rôle des fichiers d'en-tête :

(1) Appeler les fonctions de la bibliothèque via les fichiers d'en-tête. Dans de nombreux cas, il n'est pas pratique (ou interdit) de divulguer le code source aux utilisateurs, tant que les fichiers d'en-tête et les bibliothèques binaires sont fournis aux utilisateurs. Les utilisateurs n'ont qu'à appeler les fonctions de la bibliothèque en fonction de la déclaration d'interface dans le fichier d'en-tête et n'ont pas besoin de se soucier de la façon dont l'interface est implémentée. Le compilateur extraira le code correspondant de la bibliothèque.

(2) Les fichiers d'en-tête peuvent renforcer les contrôles de sécurité des types. Si une interface est implémentée ou utilisée d'une manière incompatible avec la déclaration dans le fichier d'en-tête, le compilateur signalera une erreur. Cette règle simple peut réduire considérablement la charge de débogage et de correction des erreurs du programmeur.

Par exemple, je définis une déclaration de fonction dans aaa.h, puis je crée aaa.c dans le même répertoire que aaa.h. L'implémentation de cette fonction est définie dans aaa.c, puis #. incluez cet aaa.h dans le fichier .c où se trouve la fonction principale, puis je pourrai utiliser cette fonction. Lorsque main est en cours d'exécution, il trouvera le fichier aaa.c qui définit cette fonction. En effet, la fonction principale est le point d'entrée du programme du standard C/C++, et le compilateur trouvera d'abord le fichier où se trouve la fonction.

Supposons que lorsque le compilateur compile myproj.c (qui contient main()), il constate qu'il inclut mylib.h (qui déclare la fonction void test()), alors le compilateur suivra les paramètres prédéfinis Le chemin (la liste des chemins d'inclusion et le chemin où se trouve le fichier de code) recherche un fichier d'implémentation portant le même nom (extension .cpp ou .c, dans ce cas mylib.c), si le fichier est trouvé, et le la fonction s'y trouve (void test() dans cet exemple), continuez la compilation.

Si le fichier d'implémentation est introuvable dans le répertoire spécifié, ou si le code d'implémentation n'est pas trouvé dans le fichier et les fichiers d'inclusion suivants, une erreur de compilation sera renvoyée. En fait, le processus d'inclusion peut être "vu". " en tant que Dans le processus d'épissage de fichiers, la déclaration et l'implémentation sont écrites respectivement dans le fichier d'en-tête et le fichier C, ou les deux sont écrites dans le fichier d'en-tête en même temps. En théorie, il n'y a pas de différence essentielle.

Théoriquement, tant que le contenu des fichiers C et des fichiers d'en-tête est pris en charge par le langage C, vous pouvez écrire n'importe quoi. Par exemple, si vous écrivez un corps de fonction dans un fichier d'en-tête, à condition de l'inclure. dans n'importe quel fichier C. Ce fichier d'en-tête peut compiler cette fonction dans une partie du fichier cible (la compilation est basée sur des fichiers C, si ce fichier d'en-tête n'est inclus dans aucun fichier C, ce code sera inutile), vous pouvez le faire dans le fichier C Déclaration de fonction, déclaration de variable, déclaration de structure, ce n'est pas un problème ! ! ! Alors pourquoi doit-il être divisé en fichiers d’en-tête et en fichiers C ? Et pourquoi les fonctions, les déclarations de variables, les déclarations de macro et les déclarations de structure sont-elles généralement faites dans les en-têtes ? Lors de la définition de variables dans un fichier C, qu’en est-il de l’implémentation des fonctions ? ?

Pour comprendre la différence entre les fichiers C et les fichiers d'en-tête, vous devez d'abord comprendre le processus de travail du compilateur. De manière générale, le compilateur effectuera les processus suivants :

1. étape

2. Étape d'analyse lexicale et syntaxique

3. Étape de compilation, compilez-la d'abord en instructions d'assemblage pures, puis assemblez-les en codes binaires liés au CPU pour générer chaque fichier cible

4. Dans la phase de connexion, l'adresse absolue de chaque segment de code dans chaque fichier cible est positionnée pour générer un fichier exécutable lié à une plateforme spécifique. Le compilateur compile en unités de fichiers C, c'est-à-dire si If. il n'y a pas un seul fichier C dans votre projet, alors votre projet ne sera pas compilé. Le connecteur est basé sur des fichiers cibles. Il déplacera les fonctions et les variables dans un ou plusieurs fichiers cibles pour générer le fichier exécutable final lors du développement du programme sur PC. , il existe généralement une fonction principale, qui est la convention de chaque compilateur. Afin de générer un fichier exécutable final, certains fichiers cibles sont nécessaires, c'est-à-dire des fichiers C, et ces fichiers C ont besoin d'une fonction principale comme point d'entrée du programme exécutable.

Pour faire simple, la compilation du langage C est divisée en prétraitement, compilation, assemblage et liaison (test.c test.h => test.i => test.s => test. o = > test) quatre grandes étapes. Le traitement de la macro #include dans le fichier c écrira tout le contenu du fichier h référencé dans c dans le fichier c pendant la phase de prétraitement, et générera enfin le fichier intermédiaire .i. À ce stade, le contenu du fichier h est. équivalent à être écrit dans un fichier c.

Cela fournit également un canal pour la réutilisation du code. De nombreux fichiers c peuvent référencer le même fichier h, de sorte que le fichier h sera placé dans plusieurs fichiers c et compilé plusieurs fois. être placé dans des fichiers h mais uniquement des déclarations, c'est que lorsque les définitions sont placées, elles sont compilées plusieurs fois lorsque le programme est lié (plusieurs int a ; des définitions de symboles forts sont définies dans le système), une erreur se produit et les déclarations sont. différent. Cela signifie que l'extension de la définition finira par aboutir à une seule définition, donc il n'y aura pas d'erreur de définition répétée lors de la liaison.

En programmation, il faut avoir utilisé le format suivant dans les fichiers h

#ifndef  XXX_H
#define  XXX_H
 //……
#endif

Haha, à quoi ça sert ? Il élimine les définitions en double lorsque les fichiers h se réfèrent les uns aux autres. Bien entendu, les définitions de macros jouent un rôle dans la phase de prétraitement, et il n’y a aucune ombre de macros dans le processus de post-compilation.

A.h
int a();
  
B.h
#include "A.h"
  
C.h
#include "A.h"
  
D.h
#include "A.h"
#include "B.h"

Il y aura deux instructions de int a(); répétées dans le fichier D.h ci-dessus, ce qui est un peu répétitif. C'est là que la macro de compilation conditionnelle est utile

A.h
#ifndef A_H
#define A_H
int a();
#endif

De cette façon. il n’y aura pas de duplication des définitions.

Tutoriel recommandé : "C#"

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn