为什么要在C语言中这么频繁地对结构进行typedef?


406

我看过很多程序,其中包括以下结构

typedef struct 
{
    int i;
    char k;
} elem;

elem user;

为什么经常需要它?有任何特定原因或适用范围?


14
更全面和准确的答案:stackoverflow.com/questions/612328/...
AbiusX

它具有缺点,我认为您无法使用匿名结构创建链接列表,因为struct * ptr结构内的行会导致错误
Raghib Ahsan

9
在“更全面和准确的答案”是结构和C ++中typedef结构之间的差异,并有++在这方面的C和C之间显著的差异,这使得这个问题的答案并不完全适合于约C.问题
乔纳森·莱弗勒

这个问题有重复的typedef struct vs struct定义,也有出色的答案。
乔纳森·勒夫勒

2
OTOH,kernel.org / doc / html / v4.10 / process / coding-style.html告诉我们,我们不应该进行此类typedef。
glglgl

Answers:


453

正如Greg Hewgill所说的,typedef意味着您不再需要在struct所有地方写东西。这不仅可以节省击键次数,而且还可以使代码更整洁,因为它提供了更多的smidgen抽象。

像东西

typedef struct {
  int x, y;
} Point;

Point point_new(int x, int y)
{
  Point a;
  a.x = x;
  a.y = y;
  return a;
}

当您不需要在所有地方都看到“ struct”关键字时,它会变得更加干净,看起来好像您的语言中确实存在一个名为“ Point”的类型。其中,后typedef是我猜的情况。

还要注意,尽管您的示例(和我的示例)省略了对struct 自身的命名,但实际上,为要提供不透明类型时,命名也很有用。然后,您将在标题中包含以下代码,例如:

typedef struct Point Point;

Point * point_new(int x, int y);

然后struct在实现文件中提供定义:

struct Point
{
  int x, y;
};

Point * point_new(int x, int y)
{
  Point *p;
  if((p = malloc(sizeof *p)) != NULL)
  {
    p->x = x;
    p->y = y;
  }
  return p;
}

在后一种情况下,您无法返回“按值”,因为头文件的用户不知道其定义。例如,这是GTK +中广泛使用的技术。

UPDATE请注意,也有一些备受关注的C项目,这种使用typedef隐藏的struct想法被认为是一个坏主意,Linux内核可能是最著名的此类项目。有关Linus的愤怒话,请参阅Linux Kernel CodingStyle文档的第5章。:)我的意思是,毕竟问题中的“应该”可能不是一成不变的。


58
您不应该使用带下划线后跟大写字母的标识符,它们是保留的(请参见第7.1.3节第1段)。尽管不太可能成为大问题,但使用它们在技术上却是未定义的行为(7.1.3第2段)。
dreamlax 2011年

16
@dreamlax:以防其他人不清楚,这只是以下划线和大写开头的标识符,您不应该这样做;您可以在标识符中间随意使用它。
brianmearns 2012年

12
有趣的是,此处给出的示例(其中typedef防止在各处使用“ struct”“)实际上比没有typedef的相同代码长,因为它节省了对“ struct”一词的一次使用。获得的抽象技术很少与额外的混淆技术相比具有优势。
威廉·珀塞尔

7
@Rerito fyi,C99草案的第166页,所有以下划线开头,大写字母或其他下划线开头的标识符始终保留供任何使用。并且所有以下划线开头的标识符始终保留用作普通和标记名称空间中文件范围的标识符。
e2-e4

10
有趣的是,Linux内核编码指南说我们应该对使用typedef保守一些(第5节):kernel.org/doc/Documentation/CodingStyle
gsingh2011 2013年

206

令人惊讶的是有很多人弄错了。请不要在C中进行typedef结构,它会不必要地污染全局名称空间,而该名称空间通常在大型C程序中已经非常污染。

同样,没有标记名的类型定义的结构也是在头文件之间不必要地施加排序关系的主要原因。

考虑:

#ifndef FOO_H
#define FOO_H 1

#define FOO_DEF (0xDEADBABE)

struct bar; /* forward declaration, defined in bar.h*/

struct foo {
  struct bar *bar;
};

#endif

使用这样的定义而不使用typedef,combiland单元可能会包含foo.h来获得该FOO_DEF定义。如果它不尝试取消引用结构的'bar'成员,foo则无需包含“ bar.h”文件。

另外,由于标记名和成员名之间的名称空间不同,因此可以编写非常易读的代码,例如:

struct foo *foo;

printf("foo->bar = %p", foo->bar);

由于名称空间是独立的,因此在命名与它们的struct标记名称一致的变量时不会发生冲突。

如果必须维护您的代码,则将删除您的typedef结构。


37
更令人惊讶的是,给出此答案13个月后,我是第一个赞成它的人!typedef的结构是对C的最大滥用之一,并且在编写良好的代码中没有位置。typedef对于混淆卷积函数指针类型很有用,并且实际上没有其他有用的用途。
William Pursell 2012年

28
彼得·范德·林登(Peter van der Linden)在他的启发性著作《专家C编程-深度C的秘密》中也提出反对类型定义结构的论据。要点是:您想知道某事物是结构或联合,而不是隐藏它。
詹斯2012年

34
Linux内核编码风格明确禁止类型定义结构。第5章:Typedef:“ 将typedef用于结构和指针是错误的。” kernel.org/doc/Documentation/CodingStyle
jasso

63
确切地说,一次又一次地输入“ struct”有什么好处?说到污染,为什么要在全局命名空间中拥有一个具有相同名称的结构和函数/变量/ typedef(除非它是同一函数的typedef)?安全模式是使用typedef struct X { ... } X。这样,您可以使用简短形式X在定义可用的任何地方寻址该结构,但仍可以struct X在需要时进行前向声明和使用。
帕维尔·米纳夫13'Mar

7
我个人很少使用typedef,我不会说其他人不应该使用它,这只是我的风格。我喜欢在变量类型之前看到结构,因此我马上就知道了它的结构。易于键入的参数有点la脚,具有单个字母的变量也易于键入,并且通过自动补全功能,如今在任何地方键入struct的难度都很大。
弥敦道日

138

摘自Dan Saks的旧文章(http://www.ddj.com/cpp/184403396?pgno=3):


用于命名结构的C语言规则有些古怪,但它们几乎没有害处。但是,当扩展到C ++中的类时,这些相同的规则几乎不会为爬虫提供任何裂痕。

在C中,名称出现在

struct s
    {
    ...
    };

是一个标签。标记名称不是类型名称。根据上面的定义,声明如下

s x;    /* error in C */
s *p;   /* error in C */

是C中的错误。您必须将它们写为

struct s x;     /* OK */
struct s *p;    /* OK */

联合和枚举的名称也是标签而不是类型。

在C语言中,标记与所有其他名称(对于函数,类型,变量和枚举常量)不同。C编译器将符号保存在符号表中,该符号表在概念上与包含所有其他名称的表在物理上并不分离。因此,C程序可能在相同范围内同时具有标签和另一个具有相同拼写的名称。例如,

struct s s;

是一个有效的声明,它声明类型为struct的变量s。这可能不是一个好习惯,但是C编译器必须接受它。对于C为什么如此设计,我从未见过任何理由。我一直认为这是一个错误,但是确实存在。

许多程序员(包括您的程序员在内)实际上都喜欢将结构名视为类型名,因此他们使用typedef为标签定义别名。例如,定义

struct s
    {
    ...
    };
typedef struct s S;

允许您使用S代替struct,如

S x;
S *p;

程序不能同时使用S作为类型和变量(或函数或枚举常量)的名称:

S S;    // error

很好

结构,联合或枚举定义中的标记名称是可选的。许多程序员将struct定义折叠为typedef并完全放弃了标签,如下所示:

typedef struct
    {
    ...
    } S;

链接的文章还讨论了不要求a的C ++行为如何typedef引起细微的名称隐藏问题。为了防止这些问题,typedef对于C ++中的类和结构来说,这也是一个好主意,即使乍一看似乎没有必要。在C ++中,typedef隐藏名称成为编译器告诉您的错误,而不是潜在问题的隐藏源。


2
在具有该int stat(const char *restrict path, struct stat *restrict buf)功能的(POSIX或Unix)程序中,标记名与非标记名相同的一个示例。在那里,您stat在普通名称空间和struct stat标签名称空间中都有一个功能。
乔纳森·莱夫勒

2
您的声明,SS;//错误....错误效果很好。我的意思是您的声明“我们不能为typedef标签和var使用相同的名称”是错误的...请检查
eRaisedToX

63

使用a typedef避免struct每次您声明该类型的变量时都必须编写:

struct elem
{
 int i;
 char k;
};
elem user; // compile error!
struct elem user; // this is correct

2
好的,我们在C ++中没有这个问题。那么为什么没有人从C的编译器中消除该故障并使其与C ++中的故障相同呢?好的C ++具有一些不同的应用领域,因此它具有高级功能。但是我们不能在C中继承其中的某些功能而不更改它们。原来的C?
Manoj怀疑

4
Manoj,当您需要定义引用自身的结构时,标记名称(“ struct foo”)是必需的。例如,链表中的“下一个”指针。更重要的是,编译器实现了该标准,这就是该标准所说的。
Michael Carman

41
这不是C编译器中的故障,而是设计的一部分。他们更改了C ++的代码,我认为这使事情变得简单,但这并不意味着C的行为是错误的。
爱马仕(Herms)

5
不幸的是,许多“程序员”定义了一个struct,然后使用一些“不相关”的名称对它进行了typedef(例如struct myStruct ...; typedef struct myStruct susan *;)在几乎所有情况下,typedef只会导致代码混乱,隐藏了的实际定义。变量/参数,误引线每一个人,包括代码的原作者。
user3629249

1
@ user3629249我同意您的意见,所提到的编程风格太可怕了,但这并不是typedef一般性地贬低结构的理由。您也可以这样做typedef struct foo foo;。当然,struct不需要那么多的关键字就可以了,这可能是一个有用的提示,即我们看到的类型别名是结构的别名,但是总体上来说还不错。还考虑一种情况,其中所得类型别名的标识符typedef表示它是结构fe:的别名typedef struct foo foo_struct;
RobertS支持Monica Cellio

38

始终使用typedef枚举和构造此结果的另一个很好的理由:

enum EnumDef
{
  FIRST_ITEM,
  SECOND_ITEM
};

struct StructDef
{
  enum EnuumDef MyEnum;
  unsigned int MyVar;
} MyStruct;

注意结构(Enu u mDef)中EnumDef中的错字吗?编译时没有错误(或警告),并且是正确的(取决于C标准的字面解释)。问题是我刚刚在结构中创建了一个新的(空)枚举定义。我没有(按预期)使用先前的定义EnumDef。

使用typdef时,类似的错字会导致使用未知类型的编译器错误:

typedef 
{
  FIRST_ITEM,
  SECOND_ITEM
} EnumDef;

typedef struct
{
  EnuumDef MyEnum; /* compiler error (unknown type) */
  unsigned int MyVar;
} StructDef;
StrructDef MyStruct; /* compiler error (unknown type) */

我会提倡始终使用typedef的结构和枚举。

不仅节省了一些键入操作(无双关语;)),而且因为它更安全。


1
更糟糕的是,您的错字可能与其他标签重合。在使用struct的情况下,这可能会导致整个程序正确编译并具有运行时未定义的行为。
MM

3
这个定义:'typedef {FIRST_ITEM,SECOND_ITEM} EnumDef;' 没有定义枚举。我已经编写了数百个大型程序,但不幸地对其他人编写的程序进行维护。根据经验,在结构上使用typedef只会导致问题。希望程序员没有那么多障碍,以至于他们在声明结构实例时键入完整定义时遇到问题。C不是Basic,因此键入更多字符不会损害程序的运行。
user3629249

2
对于那些讨厌输入超过绝对最小字符数的用户,我建议加入一些尝试使用最小击键次数编写应用程序的小组。只是不要在工作环境中使用他们的新“技能”,尤其是在严格执行同行评审的工作环境中
user3629249 2015年

请注意,通常不建议将其enum用作类型,因为许多编译器在“错误”使用它们时会发出奇怪的警告。例如,将an初始化enum为0可能会给出“整数常量不在枚举中”警告。enum也不允许向前声明an 。应该使用int(或unsigned int)代替。
yyny

5
该示例不会编译,我也不希望它编译。编译Debug / test.o test.c:10:17:错误:字段类型不完整'enum EnuumDef'enum EnuumDef MyEnum; ^ test.c:10:8:注意:'enum EnuumDef'的向前声明enum EnuumDef MyEnum; 产生^ 1个错误。gnuc,标准为c99。
natersoz

30

Linux内核编码风格第5章给出了使用的优缺点(主要是缺点)typedef

请不要使用“ vps_t”之类的东西。

对结构和指针使用typedef 是错误的。当你看到一个

vps_t a;

在源代码中是什么意思?

相反,如果说

struct virtual_container *a;

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

许多人认为typedef是“帮助可读性”。不是这样 它们仅对以下有用:

(a)完全不透明的对象(使用typedef主动隐藏该对象是什么)。

例如:“ pte_t”等不透明对象,您只能使用适当的访问器函数来访问。

注意!不透明和“访问器功能”本身不好。之所以将它们用于pte_t等,是因为那里确实存在绝对零的可访问信息。

(b)清除整数类型,其中抽象有助于避免混淆,无论是“ int”还是“ long”。

u8 / u16 / u32是完美的typedef,尽管它们比这里更适合(d)类。

注意!再次-需要有一个原因。如果某事是“无符号的长”,则没有理由这样做

typedef unsigned long myflags_t;

但是,如果有明确的原因,为什么在某些情况下它可能是“ unsigned int”而在其他配置下可能是“ unsigned long”,则一定要继续使用typedef。

(c)当您使用稀疏从字面上创建用于类型检查的类型时。

(d)在某些特殊情况下,与标准C99类型相同的新类型。

尽管眼睛和大脑只需要很短的时间就习惯了'uint32_t'这样的标准类型,但是仍然有人反对使用它们。

因此,允许使用特定于Linux的'u8 / u16 / u32 / u64'类型及其与标准类型相同的带符号等效项-尽管在您自己的新代码中不是必需的。

编辑已使用一种或另一组类型的现有代码时,应遵循该代码中的现有选择。

(e)在用户空间中可以安全使用的类型。

在用户空间可见的某些结构中,我们不能要求C99类型,也不能使用上面的“ u32”形式。因此,我们在与用户空间共享的所有结构中使用__u32和类似类型。

也许还有其他情况,但是该规则基本上应该是从不使用typedef,除非您可以清楚地匹配其中一个规则。

通常,指针或具有可以合理地直接访问的元素的结构永远都不应该是typedef。


4
“不透明和“访问器功能”本身并不好”。有人可以解释为什么吗?我认为信息隐藏和封装将是一个很好的主意。
Yawar

5
@Yawar我刚刚阅读了此文档,并有完全相同的想法。当然,C不是面向对象的,但是抽象仍然是一回事。
Baldrickk

12

事实证明,这是有利有弊的。有用的信息来源是开创性的书“ Expert C Programming”(第3章)。简而言之,在C中,您具有多个名称空间:标签,类型,成员名称和标识符typedef为类型引入别名,并将其定位在标记名称空间中。即

typedef struct Tag{
...members...
}Type;

定义了两件事。标签名称空间中的一个标签,类型名称空间中的一个类型。因此,您可以同时进行Type myTypestruct Tag myTagType。类似struct Type myTypeTag myTagType不合法的声明。另外,在这样的声明中:

typedef Type *Type_ptr;

我们定义一个指向我们类型的指针。因此,如果我们声明:

Type_ptr var1, var2;
struct Tag *myTagType1, myTagType2;

然后var1var2myTagType1是指向Type的指针,但myTagType2不是。

在上述书籍中,它提到了类型定义结构不是很有用,因为它仅使程序员不必编写struct这个词。但是,与许多其他C程序员一样,我也有反对意见。当您想要在C中实现多态时,尽管有时会混淆一些名称(这就是为什么不建议在诸如内核之类的大型代码库中使用它的原因),但在这里可以找到很多细节。例:

typedef struct MyWriter_t{
    MyPipe super;
    MyQueue relative;
    uint32_t flags;
...
}MyWriter;

你可以做:

void my_writer_func(MyPipe *s)
{
    MyWriter *self = (MyWriter *) s;
    uint32_t myFlags = self->flags;
...
}

因此,您可以flags通过内部结构(MyPipe)访问外部成员()。对我来说,强制转换整个类型要比(struct MyWriter_ *) s;每次您想执行这种功能都要容易。在这些情况下,简短引用是很重要的,尤其是如果您在代码中大量采用了该技术。

最后,typedef与宏相反,ed类型的最后一个方面是无法扩展它们。例如,如果您有:

#define X char[10] or
typedef char Y[10]

然后您可以声明

unsigned X x; but not
unsigned Y y;

我们并不真正在意结构,因为它不适用于存储说明符(volatileconst)。


1
MyPipe *s; MyWriter *self = (MyWriter *) s;而且您刚刚打破了严格的别名。
Jonathon Reinhart 2015年

@JonathonReinhart可以说明如何避免这种情况,例如,举世瞩目的 GTK +如何解决它:bugzilla.gnome.org/show_bug.cgi? id=140722
underscore_d

“ typedef为类型引入了一个别名,并将其定位在标签名称空间中。即” typedef struct Tag{ ...members... }Type; “定义了两件事”没有任何意义。如果typedef定义了标签,则此处的“ Type”也应该是标签。事实是该定义定义了2个标签和1型(或2型和1个标签没有把握。): struct TagTagTypestruct Tag绝对是一种类型。Tag是一个标签。但困惑是Type标签还是类型
Qwertyzw

12

我认为typedef甚至都不可能提供前向声明。当依赖关系(已知)是双向的时,使用struct,enum和union可以转发声明。

样式:在C ++中使用typedef很有意思。当处理需要多个和/或可变参数的模板时,这几乎是必要的。typedef有助于保持命名的准确性。

在C编程语言中不是这样。typedef的使用通常无济于事,只是混淆了数据结构的使用。由于仅使用{struct(6),enum(4),union(5)}个键来声明数据类型,因此几乎没有使用struct的别名。该数据类型是联合还是结构?使用简单的非典型定义声明,您可以立即知道它是什么类型。

请注意,Linux是如何严格避免这种废话的typedef带来的。结果是极简风格和简洁风格。


10
干净不会在struct所有地方重复出现……Typedef创造了新的类型。你用什么?类型。我们不在乎它是结构,联合还是枚举,这就是我们对它进行typedef的原因。
GManNickG 2010年

13
不,我们确实关心它是结构还是联合,而不是枚举或某些原子类型。您不能将结构强制为整数或指针(就此而言,不能强制为任何其他类型),这是有时存储某些上下文所需的全部。使用'struct'或'union'关键字可以改善推理的局部性。没有人说您需要知道结构内部的内容。
Bernd Jendrissek

1
@BerndJendrissek:结构和联合与其他类型不同,但是客户端代码应该关心像a这样的两件事(结构或联合)FILE中的哪一个吗?
超级猫

4
@supercat FILE是typedef的良好用法。我认为typedef被过度使用,而不是它是该语言的错误特征。恕我直言,将typedef用于所有内容都是“投机过度”的代码味道。请注意,您将变量声明为FILE * foo,而不是FILE foo。对我来说,这很重要。
2015年

2
@supercat:“如果文件识别变量的类型为FILE而不是FILE * ...”,但这正是typedef启用的歧义!我们只是习惯于打开一个带有FILE *的文件,所以我们不必为此烦恼,但是每次添加typedef时,您都会引入另外的认知开销:该API是否需要foo_t args或foo_t *?显式地携带“结构”会改善推理的局部性,如果每个函数定义要花几个字符的话。
2015年

4

让我们从基础开始,逐步进行。

这是结构定义的示例:

struct point
  {
    int x, y;
  };

这里的名称point是可选的。

可以在定义期间或之后声明结构。

定义期间声明

struct point
  {
    int x, y;
  } first_point, second_point;

定义后声明

struct point
  {
    int x, y;
  };
struct point first_point, second_point;

现在,请仔细注意上面的最后一种情况;struct point如果您决定稍后在代码中创建该类型,则需要编写声明该类型的结构。

输入typedef。如果您打算稍后在程序中使用相同的蓝图创建新的Structure(Structure是自定义数据类型),则typedef在其定义期间使用可能是一个好主意,因为您可以保存一些向前输入。

typedef struct point
  {
    int x, y;
  } Points;

Points first_point, second_point;

命名您的自定义类型时要小心

没有什么可以阻止您在自定义类型名称的末尾使用_t后缀,但是POSIX standard保留使用后缀_t表示标准库类型名称。


3

您(可选)为结构指定的名称称为标记名称,并且如上所述,它本身不是类型。要获得该类型,需要struct前缀。

除了GTK +,我不确定标记名是否像struct类型的typedef一样常用,因此在C ++中可以识别,并且您可以省略struct关键字,也可以将标记名用作类型名:


    struct MyStruct
    {
      int i;
    };

    // The following is legal in C++:
    MyStruct obj;
    obj.i = 7;


1

typedef将不提供一组相互依赖的数据结构。您不能使用typdef执行此操作:

struct bar;
struct foo;

struct foo {
    struct bar *b;
};

struct bar {
    struct foo *f;
};

当然,您可以随时添加:

typedef struct foo foo_t;
typedef struct bar bar_t;

这到底是什么意思?


1

A> typdef通过允许为数据类型创建更有意义的同义词来辅助程序的含义和文档。此外,它们还有助于针对可移植性问题(K&R,pg147,C prog lang)对程序进行参数化。

B> 结构定义类型。结构允许将一组变量方便地分组,以便于将单个变量(K&R,pg127,C编程语言)作为一个单元进行处理

上面的A中解释了C>对结构的类型定义。

D>对我而言,结构是自定义类型或容器,集合或名称空间或复杂类型,而typdef只是创建更多昵称的一种手段。


0

原来在C99中typedef是必需的。它已经过时了,但是许多工具(ala HackRank)使用c99作为其纯C实现。并且在那里需要typedef。

我并不是说如果需求发生变化,他们应该进行更改(也许有两个C选项),我们现场学习面试的人将是SOL。


2
typedef需要在C99 中进行验证。” 你什么意思?
朱利安·洛佩兹

问题是关于C,不是C++。在Ctypedef中,“是必需的”(并且很可能总是这样)。如“必需”一样,您将无法声明变量,Point varName;并且类型必须与struct Point;没有同义typedef struct Point Point;
yyny

0

在“ C”编程语言中,关键字“ typedef”用于为某些对象(结构,数组,函数..枚举类型)声明一个新名称。例如,我将使用“ struct -s”。在“ C”中,我们经常在“ main”函数之外声明“ struct”。例如:

struct complex{ int real_part, img_part }COMPLEX;

main(){

 struct KOMPLEKS number; // number type is now a struct type
 number.real_part = 3;
 number.img_part = -1;
 printf("Number: %d.%d i \n",number.real_part, number.img_part);

}

每次我决定使用struct类型时,都需要使用关键字struct'something''name'。'typedef'将简单地重命名该类型,并且我可以在程序中每次使用该新名称。因此我们的代码将是:

typedef struct complex{int real_part, img_part; }COMPLEX;
//now COMPLEX is the new name for this structure and if I want to use it without
// a keyword like in the first example 'struct complex number'.

main(){

COMPLEX number; // number is now the same type as in the first example
number.real_part = 1;
number.img)part = 5;
printf("%d %d \n", number.real_part, number.img_part);

}

如果您有一些本地对象(结构,数组,有价值的对象)将在整个程序中使用,则只需使用“ typedef”为其命名即可。


-2

完全,在C语言中,struct / union / enum是由C语言预处理器处理的宏指令(不要误以为对待“ #include”和其他命令的预处理器)

所以:

struct a
{
   int i;
};

struct b
{
   struct a;
   int i;
   int j;
};

struct b被扩展为如下形式:

struct b
{
    struct a
    {
        int i;
    };
    int i;
    int j;
}

因此,在编译时,它在堆栈上的演变如下:b:int ai int i int j

这也是为什么很难拥有自引用结构的原因,C预处理程序在无法终止的声明循环中循环。

typedef是类型说明符,这意味着只有C编译器对其进行处理,并且可以像他想要的那样优化汇编程序代码实现。它还不会像préprocessor那样笨拙地使用par类型的成员来处理结构,而是使用更复杂的引用构造算法,因此构造如下:

typedef struct a A; //anticipated declaration for member declaration

typedef struct a //Implemented declaration
{
    A* b; // member declaration
}A;

被允许并且功能齐全。当执行线程离开初始化函数的应用程序域时,此实现还可以访问编译器类型转换并消除一些错误影响。

这意味着在C中,typedef更像C ++类,而不是孤独的结构。


3
完全没有自我参照结构并不困难。struct foo {struct foo * next; 诠释事物 }
Bernd Jendrissek

4
...什么?用预处理器来描述structsand typedef的分辨率已经足够糟糕了,但是您的其余部分却是如此混乱,以至于我很难从中获取任何消息。有一件事我可以说,不过,是你的想法,非typedefd struct不能前置声明或用作不透明(指针)成员是完全错误的。在您的第一个示例中,struct b可以简单地包含struct a *,而不是typedef必需的。断言structs只是宏观扩张的愚蠢片段,并typedef赋予它们革命性的新力量,这些
underscore_d
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.