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
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.
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 struct
s than enum
s 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.
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.
For struct
s 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;
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.
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...