What is the meaning of `struct X typedef` vs. `typedef struct X`?

C++CStructVisual Studio-2010

C++ Problem Overview


I have the following (working) code in an existing code base, used in include file that is shared between C and C++, compiling on MSVC (2010) and Windows DDK:

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

And:

enum MY_ENUM {
    enum_item_1,
    enum_item_2 
} typedef MY_ENUM;

As far as I know, correct definition should look like this:

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

Is there any purpose for having the form below? Am I missing something?

C++ Solutions


Solution 1 - C++

The fact that both typedef <type> <alias> and <type> typedef <alias> are valid simply comes from the language grammar definition.

typedef is classified as a storage-class specfifier (just like static, auto), and the type itself is known as the type-specifier. From the syntax definitions in section 6.7 of the standard, you'll see that these are free to be interchanged:

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

(Note, of course, that this is equally true for structs and for non-structs, meaning that double typedef trouble; is also valid.)

Solution 2 - C++

As others said, typedef is a storage-class specifier and as with other storage-class specifiers you are also allowed to put the specifier between the type and the declarator.

While this is valid and it is also a form that should be avoided as C marked it as an obsolescent feature:

>(C11, 6.11.5p1) "The placement of a storage-class specifier other than at the beginning of the declaration specifiers in a declaration is an obsolescent feature."

Solution 3 - C++

Both have the same meaning. Both of these two forms are valid:

typedef <existing_type> <new_type>
<existing_type> typedef <new_type>   

You can typedef the above struct in either ways:

struct X {
    USHORT x;
}typedef X, *PX;     // <existing_type> typedef <new_type> 

or

typedef struct {
    USHORT x;
} X, *PX;            // typedef <existing_type> <new_type>

Solution 4 - C++

You really are allowed to put all the declaration specifiers in any order you want! The positions of any * pointers and the actual declarator (the variable or new type name) matter, but all that typedef int unsigned const static etc. stuff can be in any order.

If you look at the official grammar of C, it just says:

declaration:
    declaration-specifiers init-declarator-list ;

The declaration-specifiers are all the storage class specifiers (typedef, extern, etc.), type specifiers (the actual type, like int or struct X), type qualifiers (const and volatile), and a few other less common ones. Their order is not important. The second part is the init-declarator-list, and it's the variable or new type name (in the case of a typedef), any * characters, the initialization of the variable (int x = 3), and more. The order of things in the declarator part is important, but not the order in the declaration specifiers.

Solution 5 - C++

Disclaimer: This is not a technical but a practical answer. Refer to the other answers for technical matters. This answer reads opinionated and subjective but please bear with me while I try to explain the bigger picture.

struct is a strange beast because the stuff you put between the closing bracket } and the semicolon ; refers to the content inside or before those brackets. I know why that is, and grammatically it does make sense, but personally I find it very counter-intuitive as curly brackets usually mean scope:

Counter-intuitive examples:

// 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;

There are so many subtle things that can go wrong with the dense syntax of structs and typedefs if used like this. As shown below it is very easy to declare a variable instead of a type by accident. The compiler is only of limited help because almost all combinations are grammatically correct. They just don't necessarily mean what you try to express. It is a pit of despair.

Wrong examples:

// 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 compiles fine
typedef struct Foo {
    int x, y;
};

// compiler error
typedef Foo struct {
    int x, y;
};

For reasons of readability and maintenance I prefer to declare everything separately and never put anything behind the closing curly bracket. The cost of additional lines of code are easily outweighed by intuitive syntax. I argue that this approach makes it easy to do the right things and annoying to do the wrong things.

Intuitive examples:

// 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;
v.x = 0;
v.y = 1;

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionItaypkView Question on Stackoverflow
Solution 1 - C++Oliver CharlesworthView Answer on Stackoverflow
Solution 2 - C++ouahView Answer on Stackoverflow
Solution 3 - C++haccksView Answer on Stackoverflow
Solution 4 - C++librikView Answer on Stackoverflow
Solution 5 - C++Jonas BötelView Answer on Stackoverflow