C中这两种typedef样式有什么区别?

时间:2010-07-06 21:32:45

标签: c objective-c typedef

我很好奇当typedefing一个枚举或结构时,这里的区别是什么。这两个块之间在语义上有什么区别吗?

此:

typedef enum { first, second, third } SomeEnum;

和此:

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

结构相同的交易。我已经看到两者都在使用,他们似乎都在C或Objective-C中做同样的事情。是否存在真正的差异,或者只是偏好您可以使用哪种风格?

6 个答案:

答案 0 :(得分:15)

不同之处在于第二种方法声明了一个名为enum SomeEnum的类型,并且还声明了一个typedef-name SomeEnum - 该类型的别名。它实际上可以组合成等效的单行

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

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

第一种方法仅为原始匿名枚举类型声明typedef-name SomeEnum,这意味着您只能使用SomeEnum e声明。

因此,只要您在声明中仅使用typedef-name SomeEnum,两者之间就没有区别。但是,在某些情况下,您可能必须使用类型enum SomeEnum的完整原始名称。在第一种方法中,该名称不可用,因此您将失去运气。

例如,如果在上述声明之后,您还在某个嵌套范围中声明了一个名为SomeEnum的变量

int SomeEnum;

变量的名称将隐藏枚举的typedef-name,从而使此声明非法

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

答案 1 :(得分:7)

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

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

enum SomeEnum foo;

据我所知,代码中它们之间没有其他语义差异。对于结构和联合,递归类型需要第二种形式,可能与一个声明中的typedef结合使用

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

普通标识符在struct的说明符中尚不可见,因此您需要通过已声明的标记标识符来引用该结构。标签标识符通过在“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 ++相反),并且它们需要引用现有类型,以便它们不能用于创建前向声明。

答案 2 :(得分:4)

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

enum SomeEnum x;

而第一个仅支持

SomeEnum x;

对于长期以来一直在写C的人来说,定义struct而没有struct关键字通常会“觉得”很奇怪......

答案 3 :(得分:2)

第一个表单会创建匿名enum类型,并为其创建SomeEnum别名。

第二种表单会为其创建enum SomeEnum类型和SomeEnum别名。

(在C中,类型有单独的名称空间。也就是说,struct Fooenum Foo不同,后者与Foo不同。)

对于struct而言,这比enum更重要,因为如果struct是自我引用的,您需要使用第二种形式。例如:

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

typedef struct LinkedListNode LinkedListNode;

第一种形式无法实现上述目标。

答案 4 :(得分:0)

对于struct而言,真正的区别不仅仅在于命名。

这是有效的C:

struct SomeEnum { struct SomeEnum *first; };

这不是:

typedef struct { SomeEnum *first; } SomeEnum;

答案 5 :(得分:0)

添加到user207442的注释,源代码模块可以声明类型为“struct foo *”的变量,而 ever 具有struct的定义。这样的模块将无法取消引用这些指针,但可以将它们传递给其他模块或从其他模块传递。

例如,可以使用“typedef struct _USERCONSOLE * USERCONSOLE;”来为头文件定义类型“USERCONSOLE”。代码表示#include的头文件可以包含USERCONSOLE类型的变量,并将这些变量传递给/知道_USERCONSOLE实际上是什么的模块,而不必让头文件暴露结构的实际定义。