J’utilisais typedef pour les structures partout dans mon application. J’ai ensuite commencé à refactoriser plusieurs fichiers d’en-tête lorsqu’il a commencé à devenir maladroit. J’ai remarqué que j’avais besoin de déclarer Object, et Klass. Eh bien, à ma grande surprise, je ne pouvais pas déclarer Object ou Klass. En effet, comme vous pouvez le constater dans la structure Object et Klass, j’utilise un typedef Object et Klass.
//Klass.h typedef struct Klass Klass_t; struct Klass { void (*_initialize)(Object_t* object, Klass_t* klass); }; //Object.h typedef struct Object Object_t; struct Object { Klass_t* _klass; };
Au début, utiliser Typedef était génial. Mais en essayant de transmettre déclarer Object:
struct Object_t;
Ne fonctionne pas car il me faudrait réécrire par les déclarations de fonction comme:
void (*_initialize)(struct Object_t* object, Klass_t* klass);
J’ai donc décidé de simplement taper Object dans le fichier d’en-tête Klass.h:
typedef struct Object Object_t;
Eh bien, lorsque tous les fichiers d’en-tête sont inclus dans mon fichier Main.c, cela complian:
Object.h:5: error: redefinition of typedef 'Object_t'
Alors, j’ai alors décidé de simplement supprimer toutes les struct typedefs et de déclarer explicitement mes structures.
Existe-t-il un moyen de typer une structure et d’envoyer une déclaration dans un autre fichier sans utiliser explicitement struct Object?
Je veux conserver la structure typedefs à l’intérieur du fichier d’en-tête où la structure est déclarée. Si je dois regrouper toutes les typedef dans un fichier d’en-tête, je préférerais ne pas utiliser typedef du tout. Quoi qu’il en soit, merci pour votre temps.
Rappelez-vous qu’une typedef
est simplement un nom alternatif pour un type. Il y a une raison pour laquelle le kernel Linux n’utilise pas typedef
s pour les types de structure, et vous le démontrez.
Votre problème peut être résolu de plusieurs manières. Je remarque que C11 autorise plusieurs occurrences du même typedef
, mais je suppose que vous êtes coincé avec un compilateur plus ancien qui ne le supporte pas.
Même si le kernel Linux n’utilise pas de typedef
, j’utilise généralement typedef
, mais j’évite surtout les types de structure mutuellement référentiels (je ne peux penser à aucun code dans lequel j’ai utilisé un tel type). Et, comme le note Jens Gustedt dans sa réponse , j’utilise presque toujours les notations:
typedef struct SomeTag SomeTag;
de sorte que le nom du type et la balise de structure soient identiques (ils se trouvent dans des espaces de noms différents). Cette opération n’est pas nécessaire en C ++. lorsque vous définissez la struct SomeTag
ou la class SomeTag
, le nom SomeTag
devient un nom de type sans nécessiter de typedef
explicite (même si un typedef
ne nuit pas à révéler que l’auteur est plus expérimenté en C que C ++, ou le code créé en C code).
J’observe également qu’il vaut mieux traiter les noms commençant par un trait de soulignement comme “réservés à la mise en oeuvre”. Les règles sont un peu plus complexes que cela, mais vous courez le risque que l’implémentation usurpe vos noms – et que vous ayez le droit d’usurper vos noms – lorsque vous utilisez des noms commençant par un trait de soulignement, alors ne le faites pas. De même, POSIX réserve les noms de types se terminant par _t
pour la mise en œuvre si vous incluez des en-têtes POSIX (tels que
lorsque vous ne comstackz pas en mode ssortingct C uniquement). Évitez de créer de tels noms. ils te feront mal tôt ou tard. (Je n’ai pas corrigé le code ci-dessous pour traiter l’un ou l’autre de ces problèmes: caveat emptor !).
Dans les fragments de code ci-dessous, j’ignore principalement le code empêchant les inclusions multiples (mais vous devriez l’avoir dans votre code).
typedef struct Object Object_t; typedef struct Klass Klass_t;
#include "typedefs.h" struct Klass { void (*_initialize)(Object_t *object, Klass_t *klass); };
#include "typedefs.h" struct Object { Klass_t *_klass; };
Cela fonctionne parce que les deux noms de types Klass_t
et Object_t
sont déclarés avant leur utilisation.
struct Object
dans un prototype typedef struct Klass Klass_t; struct Object; struct Klass { void (*_initialize)(struct Object *object, Klass_t *klass); };
Ou, par souci de cohérence, il pourrait même utiliser:
void (*_initialize)(struct Object *object, struct Klass *klass);
#include "klass.h" struct Object { Klass_t *_klass; };
Cela fonctionne parce que (dans de larges limites – fondamentalement, si les types sont définis dans la scope du fichier, pas dans une fonction), struct Object
fait toujours référence au même type, que tous les détails soient ou non complètement définis.
Sous tous -std=c89
, -std=c99
et -std=c11
, GCC 4.8.2 accepte les typedef
s répliqués, comme dans le code ci-dessous. Il faut -std=c89 -pedantic
ou -std=c99 -pedantic
pour obtenir des erreurs sur le typedef
s répété.
Même sans l’option -pedantic
, GCC 4.5.2 rejette ce code; Cependant, GCC 4.6.0 et les versions ultérieures l’acceptent sans l’option -pedantic
.
#ifndef KLASS_H_INCLUDED #define KLASS_H_INCLUDED typedef struct Klass Klass_t; typedef struct Object Object_t; struct Klass { void (*_initialize)(Object_t *object, Klass_t *klass); }; #endif /* KLASS_H_INCLUDED */
#ifndef OBJECT_H_INCLUDED #define OBJECT_H_INCLUDED typedef struct Klass Klass_t; typedef struct Object Object_t; struct Object { Klass_t *klass; }; #endif /* OBJECT_H_INCLUDED */
#include "klass.h" #include "object.h" Klass_t k; Object_t o;
Vous devrez décider si vous êtes prêt à courir un risque pour votre code – quelle est l’importance de la portabilité et quelles versions de C (et quels compilateurs C) doivent-elles être portables?
Il vous manque le mot struct
clé struct
. CA devrait etre
typedef struct Object Object_t;
La déclaration anticipée de cette manière (mais voir ci-dessous) devrait toujours être possible. Ce forward déclare l’identifiant de typedef
et la balise struct
en même temps.
Il suffit de mettre de telles déclarations en avant de toutes vos struct
avant les vraies déclarations. Tant que vous n’utilisez que des pointeurs sur ces struct
dans les déclarations, tout devrait bien se passer.
nitpick: les noms avec _t
sont réservés par POSIX. Cela signifie que vous devriez l’éviter, car un jour sur une plate-forme, un Object_t
pourrait être prédéfini et Object_t
conflit avec votre type.
Personnellement, je préfère la convention suivante
typedef struct Object Object;
si bien que le mot Object, avec ou sans struct
, se réfère toujours au même.