2010-07-06 73 views
13

我很好奇这里的区别是什么时,typedefing枚举或结构。这两个块之间在语义上是否有区别?C中这两个typedef样式有什么区别?

此:

typedef enum { first, second, third } SomeEnum; 

这:

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

同样的协议的结构。我已经看到两个都在使用,他们似乎在C或Objective-C中做了同样的事情。有没有真正的差异,或者它只是您可以使用哪种风格的首选?

回答

15

区别在于第二种方法声明了名为enum SomeEnum的类型,并且还声明了typedef名称SomeEnum - 该类型的别名。它实际上可以组合成一个相当于班轮

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

这使得它相当明显的是,这两种方法之间的唯一区别是是否有在enum关键字后面的名称。使用第二种方法,您可以使用SomeEnum eenum SomeEnum e(无论您喜欢哪个)声明该枚举类型的对象。

第一种方法仅声明typedef-name SomeEnum为最初的匿名枚举类型,这意味着您仅限于SomeEnum e声明。

因此,只要您在声明中只使用typedef-name SomeEnum,两者之间就没有区别。但是,在某些情况下,您可能需要使用类型enum SomeEnum的完整原始名称。在名字不可用的第一种方法中,所以你会走运。

例如,如果上述声明之后,你还声明在一些嵌套的范围

int SomeEnum; 

变量的名称将隐藏枚举的typedef的名称命名SomeEnum变量,从而使这一声明非法

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

但是,如果你声明枚举时使用的第二种方法,您可以使用完整的类型名称

解决此问题
enum SomeEnum e; /* OK */ 

如果您在声明枚举类型时使用第一种方法,则这是不可能的。

当结构使用时,struct后的名称是必须的,当你需要一个自引用类型(即包含一个指向同一类型的类型),像

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

最后,在第二种方法typedef名称是完全可选的。您可以简单地声明

enum SomeEnum { first, second, third }; 

并且只是在每次需要引用此类型时都使用enum SomeEnum

7

是的,存在语义差异。第二个片段声明了一个标签标识符,但第一个没有。两者都声明一个普通的标识符。

这意味着,第一个,这个代码是无效的,但第二,它是:

enum SomeEnum foo; 

据我所知,没有在你的代码它们之间没有其他的语义差别。对于结构和联合,第二种形式,也许在一个声明中的typedef相结合,需要对递归类型

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

普通标识符没有在结构中的符可见,这样的话你需要参考由已经声明的标签标识符构造。标签标识符是通过在“struct”,“union”或“enum”之前加引号来引用的,而普通标识符是不带前缀的(因此名称为“普通”)。

除了分离引用的那些参考值结构,联合和枚举的标识,标签标识也可用于创建前向声明:

/* forward declaration */ 
struct foo; 

/* for pointers, forward declarations are entirely sufficient */ 
struct foo *pfoo = ...; 

/* ... and then later define its contents */ 
struct foo { 
    /* ... */ 
}; 

typedef名称不能在多次宣布相同的范围(与C++相反,它们可以),并且它们需要引用现有类型,以便它们不能用于创建前向声明。

4

唯一真正的区别是,在第二种情况下,你可以使用类似:而第一只支持

enum SomeEnum x; 

SomeEnum x; 

的人谁一直在写很长一段时间,定义一个struct而没有struct关键字往往“感觉”奇怪......

2

第一种形式创建一个匿名enum类型并为其创建一个SomeEnum别名。

第二种形式为其创建enum SomeEnum类型和SomeEnum别名。

(在C中,类型有单独的命名空间,即struct Foo不同于enum Foo,与Foo不同。)

这对于struct s比enum更重要,因为如果您的struct是自引用的,则需要使用第二个表单。例如:

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

typedef struct LinkedListNode LinkedListNode; 

以上将不可能与第一种形式。

0

对于struct s有一个真正的区别,不仅仅是命名。

这是合法的C:

struct SomeEnum { struct SomeEnum *first; }; 

这不是:

typedef struct { SomeEnum *first; } SomeEnum; 
0

添加到user207442的评论,有可能为源代码模块声明类型的变量“结构FOO *”无有结构的定义。这样的模块将无法取消引用这些指针,但可能会将它们传递给其他模块。

例如,可以使用“typedef struct _USERCONSOLE * USERCONSOLE;”来定义类型为“USERCONSOLE”的头文件。包含该头文件的代码可以包含USERCONSOLE类型的变量,并将这些变量传递给/知道_USERCONSOLE实际是什么的模块,而无需头文件公开实际的结构定义。