C语言 使用匿名结构与带typedef的命名结构

qlfbtfca  于 2023-10-16  发布在  其他
关注(0)|答案(6)|浏览(149)

什么时候应该使用以下语句之一而不是其他语句?

typedef struct Foo {
    int a;
} Bar;

typedef struct {
    int a;
} Bar;

然后用它来

Bar bar1 = { 5 };

我知道第二个是一个匿名结构,但不确定什么时候应该使用一个。

h7appiyu

h7appiyu1#

它们几乎是等同的。事实上,你可以而且应该在两个地方使用相同的名称。使用相同的名称,除非你能想出一个很好的理由。
需要非匿名的一种情况是,当你想要指向同一类型的对象的指针时,比如在链表中。

typedef struct Node {
    struct Node* next;
    int data;
} Node;

一个替代方案:

typedef struct Node Node;

struct Node {
    Node * next;
    int data;
};

根据Linus Torvalds的说法,你应该避免类型定义结构,除非你想隐藏它。来自Linux内核编码风格指南:
请不要使用vps_t之类的东西。对结构和指针使用typedef是错误的。当你在源代码中看到vps_t a;时,它意味着什么?相反,如果它写的是struct virtual_container *a;,你实际上可以知道a是什么。
很多人认为typedef有助于可读性。并非如此。它们仅用于:
a)完全不透明的对象(typedef被主动用来隐藏对象是什么)。
...
根据这一点,你永远不应该使用匿名结构体,并且typedef是严格用于接口的。所以它应该看起来像这样:

typedef struct Node {
    struct Node* next;
    int data;
} Node;

但是如果你真的要创建一个接口,一般来说你应该把它分成一个头文件和一个源文件。在这种情况下,将typedef放在头文件中,并且在源文件中根本不使用typedef:ed类型。
.c

struct Node {
    struct Node* next;
    int data;
} Node;

void insert(struct Node* head, int data) 
{
// Code
}

.h

typedef struct Node Node;

void insert(Node* head, int data);

考虑到以上所有情况,使用匿名结构的唯一有效情况是,如果你像这样同时声明一个对象:

struct {
    int data;
    float more_data;
} myObject;
xcitsw88

xcitsw882#

其实也没什么关系。如果你使用带标签的表单,你可以在struct Foo(AKA Bar)中有指向struct Foo的指针。

typedef struct Foo{
  int a;
  struct Foo *foop;
} Bar;

但是第二种形式是不可能的

typedef struct {
  int a;
  //Baz *p; not valid here since Baz isn't a typename yet
} Baz;

有些代码库根本不喜欢使用typedef s,而只是每次都用struct关键字拼写struct Foo
此外,对于第一种形式,您可以通过标记(struct Foo)或typedefsBar或任何将来/以前的typedef s)引用类型(您可以在提供定义之前执行typedef struct Foo PreviousTypedef;)。
另一方面,对于第二种形式,您只能使用Baztypedef和可能的未来typedef s(您不能转发-typedef结构体,因为它没有标记)。
(Note typedef并没有真正定义C中的类型。struct optional_tag { /*...*/ }部分可以。相反,typedef提供了类型别名(因此可能应该命名为typealias)。

mtb9vblg

mtb9vblg3#

有一次需要前者的是如果你正在制作一个链表:

typedef struct list {
    int data;
    struct list *next;
} list;

typedef list在结构定义中不可见,因此需要使用实际的结构名称来创建指向它的指针。
如果你没有这样的结构,你可以使用任何一个。
但是,你不应该使用以下划线开头的标签名称,即:

typedef struct _list {
    int data;
    struct list *next;
} list;

因为以下划线开头的名称是由实现保留的。

pkwftd7m

pkwftd7m4#

术语 anonymous struct 已经用于其他用途:在嵌套的结构(或联合)中,它们根本没有名称,并且其字段被引用,就好像它们是父级中的条目一样。
关于什么时候使用一个或另一个的实际问题是,如果你想在它里面添加一个指向自己类型的指针,你必须使用第一种形式:

typedef struct Foo { struct Foo* Child; ... } Foo;

然而,我更喜欢用这样的typedef来做这件事:

typedef struct Foo Foo;
struct Foo {Foo* Child;};
13z8s7eq

13z8s7eq5#

很多其他人都在关注它的自引用方面,但避免这样做的另一个原因是,由于C中缺乏名称空间。在某些圈子里,标准的做法是不引用typedef结构以避免struct限定符,而是引用具有完整说明符的结构(例如void foo(struct Foo* foo_ptr))。所以如果你想保持这样的风格,你就不能选择滥用匿名结构,所以:

typedef struct {
  int a;
} Bar;

Bar bar1 = {5};

应该总是

struct Bar{
  int a;
};

struct Bar bar1 = {5};

否则,你甚至不能编译bar1的示例化,而去掉struct限定符

vcudknz3

vcudknz36#

当创建一个不透明的数据类型时,头文件只包含struct的前向声明,其成员的实际定义在源文件中。因为你不能向前声明一个typedef,你必须给予一个struct一个名字。范例:
Foo.h

typedef struct Foo_ Foo;

Foo.c

struct Foo_ {
    int a;
};

当你有一个递归的数据结构,如链表,其他人都提到过。

相关问题