【问题标题】:Why are typedef names used twice in struct declaration in C?为什么 typedef 名称在 C 中的结构声明中使用了两次?
【发布时间】:2015-12-01 00:53:28
【问题描述】:

在研究 C 中的队列时,我遇到了一个类似于下面的示例。为什么结构在花括号的开头和之后都命名?为什么在添加相同类型的项目时在结构内部再次使用结构类型?这些东西是多余的还是有道理的?

typedef void* vpoint_t;

typedef struct queue_item_t{
  vpoint_t void_item;
  struct queue_item_t* next;
} queue_item_t;

【问题讨论】:

  • 建议不要使用结构名称和相同的类型名称。使用typedef struct QUEUE_ITEM {...} queue_item_t;
  • 投票重新开放,因为被重复的问题没有问同样的问题。
  • @JonathonReinhart 没错!当我收到菜单栏通知说问题已关闭时,我正在输入答案
  • @PaulOgilvie 您不应该以 _t 结束您的类型,因为它是由 POSIX 保留的。

标签: c struct typedef


【解决方案1】:
typedef struct queue_item_t {     // 1
  vpoint_t void_item;
  struct queue_item_t* next;      // 2
} queue_item_t;                   // 3

首先,请注意,整个语句定义了一个typedef

3) 是说我们正在定义的新类型(通过typedef命名为queue_item_t

1) 结构的名称(我们正在给它一个新名称)命名为struct queue_item_t。这是它的全名,包括前面的struct

2) 因为新类型还不存在(请记住,我们仍在定义它的过程中),我们必须使用它迄今为止唯一的名称,即struct queue_item_t,来自1)


请注意,您可以有匿名struct 定义,这允许您在1) 中省略名称。一个简单的例子:

typedef struct {
   int x, y, z;
} vector3;

但是,在您的示例中,由于我们需要能够引用自身的结构,因此 next 指针必须具有已定义的类型。我们可以通过前向声明结构体,对它进行类型定义,然后使用typedefd 类型为next 定义结构体:

struct _queue_item;                           // 4

typedef struct _queue_item queue_item_t;      // 5

struct _queue_item {                          // 6
  vpoint_t void_item;
  queue_item_t* next;                         // 7
}

4) 声明struct _queue_item 存在,但尚未提供它的定义。

5) typedef queue_item_tstruct _queue_item 相同。

6)现在给出结构的定义...

7) ...使用我们的typedef'd queue_item_t


说了这么多……在我看来,please don't use typedefs for structs

struct queue_item {
    void *data;
    struct queue_item *next;
}

简单而完整。您可以设法输入这六个额外的字符。

来自Linux Kernel coding style

第 5 章:类型定义

请不要使用“vps_t”之类的东西。 将 typedef 用于结构和指针是一个错误。当你看到一个

  vps_t a;

在源代码中,它是什么意思? 相反,如果它说

  struct virtual_container *a;

您实际上可以分辨出“a”是什么。

有一些例外,您可以阅读。


最近的一些相关问题:

【讨论】:

  • 注意:我认为这里关于struct 的所有内容都适用于union
  • 有规律地使用 typedef 是可以的。 Linus 不喜欢它。仅在编写 linux 内核代码时才重要。疯狂的是混合“typedef struct foo {..} foo_t”与“typedef struct bar { ...} * bar_t”和类似的代码混淆。或者像“#define foo_t struct foo”这样的废话。
【解决方案2】:

让我们稍微修改一下声明,让讨论更容易理解:

typedef struct queue_item {
  vpoint_t void_item;
  struct queue_item* next;
} QueueItemType;

C 支持几种不同的命名空间;为联合、结构和枚举类型上的标记名称保留一个名称空间。在这种情况下,标签名称为queue_item。另一个名称空间是为常规标识符保留的,包括 typedef 名称,例如 QueueItemType

next 成员被用来指向另一个 struct queue_item 类型的实例(即队列中的下一个项目)。它被声明为指向struct queue_item 的指针有两个原因:

  • 结构类型不能包含自身的实例;一方面,类型必须无限大(struct queue_item 包含一个成员 next,这是一个 struct queue_item,其中包含一个成员 next,这是一个 struct queue_item,其中包含一个成员 next , 无限);

  • 结构类型定义直到结束}完整,并且您不能声明不完整类型的实例。但是,您可以声明一个指向不完整类型的 指针,我们在下面这样做:

    struct queue_item *next;

为什么不用QueueItemType *next; 而不是struct queue_item *next?同样,在声明 next 时,结构类型定义并不完整; typedef 名称 QueueItemType 尚不存在。但是,标签名称queue_item 已经对编译器可见,因此我们可以使用struct queue_item 类型声明指针。

由于标记名称和 typedef 名称占用不同的名称空间,因此可以为标记名称和 typedef 名称使用相同的名称而不会发生冲突。编译器通过 struct 关键字的存在来消除两者之间的歧义。

【讨论】:

    【解决方案3】:

    首先,我建议永远不要使用结构名称和相同的类型名称。使用typedef struct QUEUE_ITEM {...} queue_item_t;

    至于问题:如果你想做一个“递归数据结构”,也就是一个有指向自身实例的指针的数据结构,那么你必须能够告诉编译器“这个字段是一个指向我们自己。你还不完全了解我们的样子,因为我仍在定义它,所以只需为指针保留空间”。为此,您声明

    struct T {
        ...
        struct T *ptr;
        ....
    };
    

    使用最后的 } queue_item_t; 为结构创建一个新名称。

    【讨论】:

    • 你建议不要使用结构名称和相同的类型名称,为什么?
    • 因为它令人困惑并且很少知道它们在两个不同的名称空间(标签和类型定义)中。
    【解决方案4】:

    "struct foo {...}" 是一回事。它定义了一个结构,你需要在你使用它的每个地方输入“struct foo”。

    "typedef ... foo" 定义了一个新类型,因此您只需在使用它的地方键入“foo”即可。

    “typedef struct foo {...} foo”是一个习惯用法,因此您可以同时使用两者,很可能只是“foo”来节省击键和视觉污染。

    【讨论】:

    • typedef struct foo { struct foo *next } foo; 编译,typedef struct { foo *next } foo; 不编译,所以这个习语的目的是允许一个指向结构的指针包含在结构本身中。
    猜你喜欢
    • 1970-01-01
    • 2011-10-12
    • 2013-07-17
    • 2023-03-19
    • 2019-04-26
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 2023-01-27
    相关资源
    最近更新 更多