web-dev-qa-db-fra.com

redéfinition de typedef

Je fais peut-être cela incorrectement et c'est beaucoup une question de savoir pourquoi cela fonctionne dans un compilateur et pas dans l'autre.

J'ai une grande application C et j'essaie de suivre le style de ne pas inclure les fichiers d'en-tête à partir d'autres fichiers d'en-tête. Au lieu de cela, en utilisant des déclarations avancées; ainsi j'essaye ce qui suit.

// in A.h
typedef struct A_ A;
typedef struct B_ B;
struct A_ {
    double a;
    B *b;
};

// in B.h
typedef struct B_ B;
struct B_ {
    int c;
};

// in C.h
typedef struct A_ A;
typedef struct B_ B;
void function_do_something(A*, B*);

// in C.c
#include "A.h"
#include "B.h"
#include "C.h"
void function_do_something(A* a, B* b) {
    ...
}

Ce paradigme se compile et s'exécute dans Ubuntu 11.10 gcc - mais il donne des erreurs de compilation dans OpenSUSE gcc qui disent "redéfinition de typedef".

J'ai fait mon développement dans Ubunutu et je n'avais donc pas réalisé que ce paradigme pouvait être incorrect. Est-ce juste que c'est faux et que le gcc d'Ubuntu est trop gentil?

20
BrT

Cela m'a surpris car je suis assez sûr que la redéclaration du même typedef dans la même portée est légale en C++, mais apparemment ce n'est pas légal en C.

Tout d'abord, les noms de typedef n'ont aucun lien:

ISO/CEI 9899: 1999 + TC3 6.2.6/6:

Les identifiants suivants n'ont pas de lien: un identifiant déclaré autre chose qu'un objet ou une fonction [...]

et 6,7/3:

Si un identifiant n'a pas de lien, il ne doit pas y avoir plus d'une déclaration de l'identifiant (dans un déclarant ou spécificateur de type) avec la même portée et dans le même espace de nom, à l'exception des balises spécifiées au 6.7.2.3.

Vous devez donc vous assurer que chaque déclaration typedef n'apparaît qu'une seule fois à la portée du fichier dans chaque unité de traduction.

24
CB Bailey

Il manque un morceau de l'idiome. Les déclarations avancées sont indépendantes des définitions, elles doivent donc figurer dans un fichier d'en-tête distinct.

// a_fwd.h

#ifndef A_FWD_H
#define A_FWD_H

typedef struct A_ A;

#endif

// a.h

#ifndef A_H
#define A_H

#include "a_fwd.h"

struct A_ {
};

#endif

Maintenant, il est toujours sûr d'inclure des en-têtes dans n'importe quel ordre.


Il est illégal d'avoir deux définitions de quoi que ce soit. Un typedef est une définition, pas seulement une déclaration, donc le seul compilateur était assez laxiste pour permettre la redondance.

5
Potatoswatter

Le compilateur ubuntu est trop souple; vous ne pouvez pas taper deux fois la même chose. Dans le style auquel vous vous référez, l'ordre des inclusions est important et est généralement mentionné en tant que commentaire dans le fichier d'en-tête ou dans la documentation. Dans ce cas, vous auriez:

//A.h
typedef struct A A;
struct A {
    double a;
    B* b;
};

// B.h
typedef struct B B;
struct B {
    int c;
};

// C.h
void function_do_something(A*, B*);

// C.c
#include "B.h"
#include "A.h"
#include "C.h"

void function_do_something(A* a, B* b){ ... }

Vous pourriez noter que cela deviendra compliqué dans le cas de dépendances cycliques.

1
Dave

Juste pour une question de style, je mettrais le typedef après la structure. c'est à dire.:

struct B_ {
    int c;
};
typedef struct B_ B;

De cette façon, vous dites: "voici B_ et maintenant je veux l'appeler B". Il se pourrait que l'inverse trompe quelque chose dans le compilateur.

1
Rob

Vous redéfinissez A et B en écrivant la même instruction dans plusieurs fichiers d'en-tête. Une solution serait de supprimer le typedef de A et B des A.h et B.h et d'utiliser votre C.h tel quel.

0
Jan Henke

Comme d'autres l'ont déjà indiqué, vous ne pouvez pas redéfinir les types en C. Cette erreur indique principalement qu'il peut y avoir des inclusions en boucle ou un autre défaut logique. Pour éviter cela, la meilleure pratique consiste à verrouiller les fichiers d'inclusion, c'est-à-dire.

#ifndef __HEADER_H__
#define __HEADER_H__

// Your code goes here

#endif

De cette façon, les inclusions inutiles seront omises par ce verrou.
Dans votre exemple, vous devrez inclure B dans A et A dans C. Inclure B dans C n'aurait aucun effet et satisferait le compilateur

0
friendzis