What are the differences between these two typedef styles in C?

后端 未结 6 1269
野趣味
野趣味 2021-01-11 14:24

I\'m curious what the difference here is when typedefing an enum or struct. Is there any difference semantically between these two blocks?

This:

typ         


        
相关标签:
6条回答
  • 2021-01-11 14:33

    The difference is that the second approach declares a type named enum SomeEnum and also declares a typedef-name SomeEnum - an alias for that type. It can actually be combined into the equivalent one-liner

    typedef enum SomeEnum { first, second, third } SomeEnum;
    

    which makes it rather obvious that the only difference between the two approaches is whether there's a name after the enum keyword. With the second approach, you can declare object of that enum type by using either SomeEnum e or enum SomeEnum e, whichever you prefer.

    The first approach only declares the typedef-name SomeEnum for an originally anonymous enum type, meaning that you are limited to SomeEnum e declarations.

    So, as long as you only use the typedef-name SomeEnum in your declarations, there will be no difference between the two. However, in some cases you might have to use the full original name of the type enum SomeEnum. In the first approach that name is not available, so you'll be out of luck.

    For example, if after the above declaration you also declare a variable named SomeEnum in some nested scope

    int SomeEnum;
    

    the name of the variable will hide the typedef-name of the enum, thus making this declaration illegal

    SomeEnum e; /* ERROR: `SomeEnum` is not a type */
    

    However, if you used the second approach when declaring your enum, you can work around this problem by using the full type name

    enum SomeEnum e; /* OK */
    

    This would not be possible if you used the first approach when declaring your enum type.

    When used with structs, the name after the struct is a must when you need a self-referencing type (a type that contains a pointer to the same type), like

    typedef struct SomeStruct {
      struct SomeStruct *next;
    } SomeStruct;
    

    Finally, in the second approach the typedef name is totally optional. You can simply declare

    enum SomeEnum { first, second, third };
    

    and just use enum SomeEnum every time you need to refer to this type.

    0 讨论(0)
  • 2021-01-11 14:33

    The first form creates an anonymous enum type and creates a SomeEnum alias to it.

    The second form creates both a enum SomeEnum type and a SomeEnum alias to it.

    (In C, there are separate namespaces for types. That is, struct Foo is different from enum Foo which is different from Foo.)

    This is more important for structs than enums since you'd need to use the second form if your struct were self-referential. For example:

    struct LinkedListNode
    {
        void* item;
        struct LinkedListNode* next;
    };
    
    typedef struct LinkedListNode LinkedListNode;
    

    The above wouldn't be possible with the first form.

    0 讨论(0)
  • 2021-01-11 14:36

    Yes, there is a semantic difference. The second snippet declares a tag identifier, but the first doesn't. Both declare an ordinary identifier.

    That means that for the first, this code is not valid, but for the second, it is:

    enum SomeEnum foo;
    

    As far as i know, there is no other semantic difference between them in your code. For structs and unions, the second form, maybe combined with the typedef in one declaration, is needed for recursive types

    typedef struct node {
      struct node *parent; // refer to the tag identifier
    } node;
    

    The ordinary identifier is not yet visible in the struct's specifier, and thus you need to refer to the struct by the already declared tag identifier. Tag identifiers are referred to by prepending them by "struct", "union" or "enum", while ordinary identifiers are referred to without a prefix (thus the name "ordinary").

    Besides separating the identifiers that refer to structs, unions and enumerations from those that refer to values, tag identifiers are also useful for creating forward declarations:

    /* forward declaration */
    struct foo; 
    
    /* for pointers, forward declarations are entirely sufficient */
    struct foo *pfoo = ...;
    
    /* ... and then later define its contents */
    struct foo {
      /* ... */
    };
    

    Typedef names can't be declared repeatedly at the same scope (as opposed to C++ where they can), and they need to refer to an existing type, so that they cannot be used to create forward declarations.

    0 讨论(0)
  • 2021-01-11 14:38

    For structs there's a real difference that isn't simply about naming.

    This is valid C:

    struct SomeEnum { struct SomeEnum *first; };
    

    This is not:

    typedef struct { SomeEnum *first; } SomeEnum;
    
    0 讨论(0)
  • 2021-01-11 14:51

    Adding to user207442's comment, it's possible for a source code module to declare variables of type "struct foo *" without ever having a definition for the struct. Such a module will not be able to dereference such pointers, but may pass them to and from other modules.

    For example, one could have a header file define a type "USERCONSOLE" using "typedef struct _USERCONSOLE *USERCONSOLE;". Code that #include's that header file could have variables of type USERCONSOLE, and pass such variables to/from modules that know what a _USERCONSOLE really is, without the header file having to expose the actual definition of the structure.

    0 讨论(0)
  • 2021-01-11 14:59

    The only real difference is that in the second case, you can use something like:

    enum SomeEnum x;
    

    whereas the first only supports:

    SomeEnum x;
    

    To people who've been writing C a long time, defining a struct without the struct keyword often "feels" strange...

    0 讨论(0)
提交回复
热议问题