web-dev-qa-db-fra.com

Comment déclarer une structure dans un en-tête devant être utilisé par plusieurs fichiers dans c?

Si j'ai un fichier source.c avec une structure:

struct a { 
    int i;
    struct b {
        int j;
    }
};

Comment cette structure peut-elle être utilisée dans un autre fichier (c'est-à-dire func.c)?

Devrais-je créer un nouveau fichier d’en-tête, déclarer la structure à cet endroit et inclure cet en-tête dans func.c?

Ou devrais-je définir la structure entière dans un fichier d'en-tête et l'inclure dans les deux source.c et func.c? Comment la structure peut-elle être déclarée extern dans les deux fichiers?

Devrais-je typedef ça? Si c'est le cas, comment?

110
Manoj

si cette structure doit être utilisée par un autre fichier func.c, comment le faire?

Lorsqu'un type est utilisé dans un fichier (c'est-à-dire un fichier func.c), il doit être visible. Le pire moyen de le faire est de copier-coller dans chaque fichier source nécessaire.

La bonne façon consiste à le placer dans un fichier d'en-tête et d'inclure ce fichier d'en-tête à tout moment.

faut-il ouvrir un nouveau fichier d'en-tête et y déclarer la structure et inclure cet en-tête dans le fichier func.c?

C’est la solution que j’aime le plus, car elle rend le code hautement modulaire. Je coderais votre structure comme suit:

#ifndef SOME_HEADER_GUARD_WITH_UNIQUE_NAME
#define SOME_HEADER_GUARD_WITH_UNIQUE_NAME

struct a
{ 
    int i;
    struct b
    {
        int j;
    }
};

#endif

Je mettrais les fonctions utilisant cette structure dans le même en-tête (les fonctions qui font "sémantiquement" partie de son "interface").

Et en général, je pouvais nommer le fichier d'après le nom de la structure et utiliser à nouveau ce nom pour choisir les en-têtes définis.

Si vous devez déclarer une fonction à l'aide d'un pointeur sur la structure, vous n'avez pas besoin de la définition complète de la structure. Une simple déclaration en aval du type:

struct a ;

Sera suffisant, et cela diminue le couplage.

ou pouvons-nous définir la structure totale dans le fichier d'en-tête et l'inclure à la fois dans source.c et dans func.c?

C'est une autre façon, plus facile quelque peu, mais moins modulaire: certains codes nécessitant uniquement votre structure pour fonctionner devront toujours inclure tous les types.

En C++, cela pourrait entraîner une complication intéressante, mais ceci est hors sujet (pas de balise C++), je ne vais donc pas en dire plus.

puis comment déclarer cette structure en tant qu'extern dans les deux fichiers. ?

Je ne vois peut-être pas le problème, mais Greg Hewgill a une très bonne réponse dans son message Comment déclarer une structure dans un en-tête devant être utilisé par plusieurs fichiers dans c? .

allons-nous le saisir alors comment?

  • Si vous utilisez C++, ne le faites pas.
  • Si vous utilisez C, vous devriez.

La raison en est que la gestion de la structure C peut être pénible: vous devez déclarer le mot clé struct partout où il est utilisé:

struct MyStruct ; /* Forward declaration */

struct MyStruct
{
   /* etc. */
} ;

void doSomething(struct MyStruct * p) /* parameter */
{
   struct MyStruct a ; /* variable */
   /* etc */
}

Alors qu'une typedef vous permettra de l'écrire sans le mot-clé struct.

struct MyStructTag ; /* Forward declaration */

typedef struct MyStructTag
{
   /* etc. */
} MyStruct ;

void doSomething(MyStruct * p) /* parameter */
{
   MyStruct a ; /* variable */
   /* etc */
}

Il est important de garder un nom pour la structure. L'écriture:

typedef struct
{
   /* etc. */
} MyStruct ;

créera simplement une structure anonyme avec un nom typedef, et vous ne pourrez pas la déclarer. Alors gardez le format suivant:

typedef struct MyStructTag
{
   /* etc. */
} MyStruct ;

Ainsi, vous pourrez utiliser MyStruct où vous voulez, sans ajouter le mot-clé struct, et continuer à utiliser MyStructTag quand une typedef ne fonctionnera pas (c'est-à-dire une déclaration forward)

Modifier:

Correction de l’hypothèse erronée sur la déclaration de structure C99, comme l’a justement fait remarquer Jonathan Leffler .

Modifier le 2018-06-01:

Craig Barnes nous rappelle dans son commentaire qu'il n'est pas nécessaire de conserver des noms séparés pour le nom de structure "tag" et son nom "typedef", comme je l'ai fait ci-dessus pour des raisons de clarté.

En effet, le code ci-dessus pourrait très bien s’écrire:

typedef struct MyStruct
{
   /* etc. */
} MyStruct ;

IIRC, c’est ce que fait le C++ avec sa déclaration de structure plus simple, en coulisse, pour le maintenir compatible avec le C:

// C++ explicit declaration by the user
struct MyStruct
{
   /* etc. */
} ;
// C++ standard then implicitly adds the following line
typedef MyStruct MyStruct;

De retour en C, j'ai vu les deux utilisations (noms distincts et mêmes noms), et aucune d'elles n'a d'inconvénients, alors utiliser le même nom facilite la lecture si vous n'utilisez pas C séparez les "espaces de noms" pour structs et autres symboles .

129
paercebal

Pour une définition de structure devant être utilisée dans plusieurs fichiers source, vous devez absolument la placer dans un fichier d'en-tête. Incluez ensuite ce fichier d'en-tête dans tout fichier source ayant besoin de la structure.

La déclaration extern n'est pas utilisée pour les définitions de structure, mais pour les déclarations de variable (c'est-à-dire certaines valeurs de données avec un type de structure que vous avez défini). Si vous souhaitez utiliser la même variable dans plusieurs fichiers source, déclarez-la sous la forme extern dans un fichier d'en-tête, comme suit:

extern struct a myAValue;

Ensuite, dans le fichier source n, définissez la variable réelle:

struct a myAValue;

Si vous oubliez de le faire ou si vous le définissez accidentellement dans deux fichiers sources, l’éditeur de liens vous en informera.

33
Greg Hewgill

a.h:

#ifndef A_H
#define A_H

struct a { 
    int i;
    struct b {
        int j;
    }
};

#endif

voilà, il ne vous reste plus qu'à inclure a.h dans les fichiers où vous souhaitez utiliser cette structure.

5
fmsf