web-dev-qa-db-fra.com

Liaison de fichiers .h avec .c avec des protections d'en-tête #ifdef

je rencontre des problèmes pour lier les fichiers .h et .c, j'ai également lu quelques discussions concernant ce problème et elles sont toutes un peu vagues et je ne parviens toujours pas à en comprendre complètement le concept, et j'ai beaucoup de problèmes de liaison , disons que j'ai bc et bh que j'utiliserai en ac, et je ne sais pas s'il faut inclure bh à la fois ac et bc cuz bc lui-même a besoin de connaître la structure définie en bh, j'ai une fonction qui a son prototype en bh et est défini en bc qui utilise également la structure en bh, je ne suis pas en incluant bh en bc cuz car ce que je sais bh ressemble plus à une interface à ac qui utilisera les fonctions en bc .. Voici un plus clair exemple

fichier b.h

typedef struct{
int x, y;
}myStruct;

void funct1(myStruct);
void funct2(myStruct);

fichier b.c

void funct1(myStruct x)
{
    //do something
}

void funct2(myStruct y)
{
     //do something
} 

fichier a.c

#include "b.h"

int main()
{
myStruct x;
  funct1(x);
  funct2(y);
return 0;
}

Exécuté la commande dans cygwin: gcc b.c a.c -g

Maintenant, la partie déroutante, j'ai une erreur de liaison dans laquelle lorsque b.c est compilé, il ne peut pas détecter la structure et les prototypes dans b.h. Parce que tout ce que je sais, c'est que b.h est utilisé pour lier b.c à partir de a.c mais quand les deux .c sont compilés, il semble que b.c ne puisse pas trouver sa structure et ses prototypes,

Pourquoi n'ai-je pas inclus bh dans bc?Réponse: Cuz comme ce que je sais, bh est déjà inclus dans ac et quand je l'inclue à nouveau dans bc, je vais faire des doubles inclusions <--- c'est ce que j'apprends jusqu'à présent et je sais qu'il y a #ifdef mais il semble que cela ne fonctionnera pas, peut-être que je ne sais toujours pas comment l'utiliser, si vous savez n'hésitez pas à discuter ce.

Si vous avez une idée de la façon de procéder, n'hésitez pas à m'en parler.

il y a une directive #ifdef mais je n'arrive pas à avoir une idée de comment faire cela.

REMARQUE: ASSUMEZ QUE TOUS LES CODES CI-DESSUS IS SYNTACTIQUEMENT CORRECT s'il y a un mot mal orthographié, veuillez l'ignorer, je ne suis qu'après les inclusions entre .h et .c

13
lemoncodes

Vous devez en effet #include b.h dans b.c. Chaque fichier est compilé séparément avant que l'éditeur de liens ne prenne le relais, donc peu importe que vous ayez inclus b.h dans a.c, car b.c est compilé par lui-même et n'a aucune idée du contenu de b.h à moins de l'inclure.

Voici un exemple de #include garde

// some_header_file.h
#ifndef SOME_HEADER_FILE_H
#define SOME_HEADER_FILE_H
// your code
#endif

Lorsque some_header_file.h est inclus n'importe où, tout ce qui se trouve entre les #ifndef et le #endif sera ignoré si SOME_HEADER_FILE_H a été défini, ce qui se produira la première fois qu'il sera inclus dans l'unité de compilation.

Il est courant de nommer le #define après le nom du fichier, pour garantir l'unicité de votre projet. J'aime également le préfixer avec le nom de mon projet ou de mon espace de noms, pour réduire le risque de conflits avec un autre code.

REMARQUE: le même fichier d'en-tête PEUT être inclus plusieurs fois dans votre projet, même avec la protection d'inclusion ci-dessus, il ne peut tout simplement pas être inclus deux fois dans la même unité de compilation. Ceci est démontré comme suit:

// header1.h
#ifndef HEADER_H
#define HEADER_H
int test1 = 1;
#endif

// header2.h
#ifndef HEADER_H
#define HEADER_H
int test2 = 2;
#endif

Voyons maintenant ce qui se passe lorsque nous essayons d'inclure les deux fichiers ci-dessus. Dans une seule unité de compilation:

// a.cpp
#include "header1.h"
#include "header2.h"
#include <iostream>
int main()
{
   std::cout << test1;
   std::cout << test2;
};

Cela génère une erreur de compilation car test2 n'est pas défini - il est ignoré dans header2.h car HEADER_H est déjà défini par le temps inclus. Maintenant, si nous incluons chaque en-tête dans des unités de compilation distinctes:

// a.cpp
#include "header2.h"
int getTest2()
{
   return test2;
};

// b.cpp
#include "header1.h"
#include <iostream>
int getTest2(); // forward declaration
int main()
{
   std::cout << test1;
   std::cout << getTest2();
};

Il compile bien et produit la sortie attendue (1 et 2), même si nous incluons deux fichiers qui définissent tous deux HEADER_H.

27
JBentley

Vous devez inclure b.h dans tous les fichiers qui utilisent les structures définies dans b.h. Vous devez donc mettre un #include <b.h> dans les deux fichiers. Pour éviter cela b.h est chargé plusieurs fois, vous avez besoin des directives #ifdef. Dans ton cas:

b.h

#ifndef B_H
#define B_H

typedef struct{
    int x, y;
}myStruct;

void funct1(myStruct);
void funct2(myStruct);

#endif

et b.c:

#include "b.h"

void funct1(myStruct x)
{
    //do something
}

void funct2(myStruct y)
{
     //do something
} 
3

Un bon codage vous obligerait à inclure b.h dans b.c.

Voici un protège-tête qui devrait fonctionner:

#ifndef B_H_INCLUDED
#define B_H_INCLUDED
//header file
#endif

Mettez vos déclarations à l'endroit où se trouve le commentaire et incluez-les partout où vous en avez besoin.

EDIT La façon dont je le comprends, c'est que gcc compile d'abord b.c, car a.c dépend de b.c. Mais quand il compile d'abord b.c, b.h n'a pas encore été inclus.

1
BenjiWiebe

Tu dois #include b.h en b.c. Ce n'est pas seulement une interface pour a.c, b.c doit également connaître les mêmes définitions pour son propre code. Votre raison pour ne pas inclure b.h dans b.c est erronée. Chaque fichier .c est compilé séparément de tous les autres fichiers .c. Lorsque le compilateur a terminé avec a.c, il recommence à neuf avec b.c. Peu importe que a.c inclue b.h, car b.c n'a aucun concept que a.c existe même. Le but d'un protecteur d'en-tête est d'empêcher qu'un fichier .h ne soit traité plusieurs fois s'il est inclus plusieurs fois lors de la compilation d'un fichier .c donné. Sans la garde, les déclarations seraient compilées plusieurs fois, provoquant des erreurs sur plusieurs déclarations de symboles existants.

0
Remy Lebeau