Quelle est la signification de `struct X typedef` vs.` typedef struct X`?

J’ai le code suivant (fonctionnel) dans une base de code existante, utilisée dans le fichier include partagé entre C et C ++, compilé sur MSVC (2010) et Windows DDK:

struct X { USHORT x; } typedef X, *PX; 

Et:

 enum MY_ENUM { enum_item_1, enum_item_2 } typedef MY_ENUM; 

Pour autant que je sache, la définition correcte devrait ressembler à ceci:

 typedef struct { USHORT x; } X, *PX; 

Y a-t-il un but pour avoir le formulaire ci-dessous? Est-ce que je manque quelque chose?

Le fait que typedef et typedef soient tous deux valides provient de la définition grammaticale du langage.

typedef est classé comme spécificateur de classe de stockage (tout comme static , auto ), et le type lui-même est appelé spécificateur de type . À partir des définitions de syntaxe de la section 6.7 de la norme, vous verrez que celles-ci sont libres d’être échangées:

 declaration: declaration-specifiers init-declarator-list ; declaration-specifiers: storage-class-specifier declaration-specifiers type-specifier declaration-specifiers type-qualifier declaration-specifiers function-specifier declaration-specifiers init-declarator-list: init-declarator init-declarator-list , init-declarator init-declarator: declarator declarator = initializer 

(Notez bien sûr que cela est également vrai pour les structs et les non-structs, ce qui signifie que le double typedef trouble; est également valide.)

Comme d’autres l’ont dit, typedef est un spécificateur de classe de stockage et, comme avec d’autres spécificateurs de classe de stockage, vous pouvez également placer le spécificateur entre le type et le déclarant.

Bien que ce soit valable et que c’est aussi une forme à éviter car C la qualifie de fonctionnalité obsolète:

(C11, 6.11.5p1) “Le placement d’un spécificateur de classe de stockage autre qu’au début des spécificateurs de déclaration dans une déclaration est une fonctionnalité obsolète.”

Les deux ont la même signification. Ces deux formes sont valides:

 typedef    typedef  

Vous pouvez typedef la structure ci-dessus de l’une ou l’autre manière:

 struct X { USHORT x; }typedef X, *PX; //  typedef  

ou

 typedef struct { USHORT x; } X, *PX; // typedef   

Vous êtes vraiment autorisé à mettre tous les spécificateurs de déclaration dans l’ordre de votre choix! Les positions de tout pointeur et du déclarant réel (la variable ou le nom du nouveau type) sont importantes, mais tout ce qui est typedef int unsigned const static etc. peut être dans n’importe quel ordre.

Si vous regardez la grammaire officielle de C, cela dit simplement:

 declaration: declaration-specifiers init-declarator-list ; 

Les declaration-specifiers sont tous les spécificateurs de classe de stockage ( typedef , extern , etc.), les spécificateurs de type (le type réel, comme int ou struct X ), les qualificateurs de type ( const et volatile ) et quelques autres moins courants. Leur ordre n’est pas important. La seconde partie est la liste init-declarator-list , et c’est la variable ou le nouveau nom de type (dans le cas d’un typedef), n’importe quels caractères * , l’initialisation de la variable ( int x = 3 ), etc. L’ordre des éléments dans la partie déclarante est important, mais pas l’ordre dans les spécificateurs de déclaration.

Disclaimer: Ce n’est pas une réponse technique mais pratique. Reportez-vous aux autres réponses pour les questions techniques. Cette réponse se lit de manière subjective et subjective, mais s’il vous plaît, supportez-moi pendant que j’essaie d’expliquer la situation dans son ensemble.

struct est une bête étrange car les trucs que vous mettez entre le crochet de fermeture } et le point-virgule ; se réfère au contenu à l’intérieur ou avant ces crochets. Je sais pourquoi, et grammaticalement, cela a du sens, mais personnellement, je trouve cela très contre-intuitif car les accolades signifient généralement scope:

Exemples contre-intuitifs:

 // declares a variable named `foo` of unnamed struct type. struct { int x, y; } foo; foo.x = 1; // declares a type named `Foo` of unnamed struct type struct { int x, y; } typedef Foo; Foo foo2; foo2.x = 2; // declares a type named `Baz` of the struct named `Bar` struct Bar { int x, y; } typedef Baz; // note the 'struct' keyword to actually use the type 'Bar' struct Bar bar; bar.x = 3; Baz baz; baz.x = 4; 

Il y a tellement de choses subtiles qui peuvent mal tourner avec la syntaxe dense des struct s et typedef s si elles sont utilisées comme ça. Comme indiqué ci-dessous, il est très facile de déclarer une variable au lieu d’un type par accident. Le compilateur n’a qu’une aide limitée car presque toutes les combinaisons sont grammaticalement correctes. Ils ne veulent tout simplement pas dire ce que vous essayez d’exprimer. C’est une fosse de désespoir .

Mauvais exemples:

 // mixed up variable and type declaration struct foo { int x, y; } Foo; // declares a type 'foo' instead of a variable typedef struct Foo { int x, y; } foo; // useless typedef but comstacks fine typedef struct Foo { int x, y; }; // comstackr error typedef Foo struct { int x, y; }; 

Pour des raisons de lisibilité et de maintenance, je préfère tout déclarer séparément et ne jamais rien placer derrière le crochet. Le coût des lignes de code supplémentaires est facilement compensé par une syntaxe intuitive. Je soutiens que cette approche permet de faire les bonnes choses et d’agacer les mauvaises choses .

Exemples intuitifs:

 // declares a struct named 'TVector2' struct TVector2 { float x, y; }; // declares a type named 'Vector2' to get rid of the 'struct' keyword // note that I really never use 'TVector2' afterwards typedef struct TVector2 Vector2; Vector2 v, w; vx = 0; vy = 1;