为啥我们要在 C 中如此频繁地对结构进行 typedef 呢?

Posted

技术标签:

【中文标题】为啥我们要在 C 中如此频繁地对结构进行 typedef 呢?【英文标题】:Why should we typedef a struct so often in C?为什么我们要在 C 中如此频繁地对结构进行 typedef 呢? 【发布时间】:2010-09-20 03:33:06 【问题描述】:

我见过很多由如下结构组成的程序

typedef struct 

    int i;
    char k;
 elem;

elem user;

为什么经常需要它?有什么具体原因或适用范围?

【问题讨论】:

更全面准确的回答:***.com/questions/612328/… 它有缺点我认为你不能使用匿名结构创建链接列表,因为结构内的struct * ptr行会导致错误 “更全面、更准确的答案”是Difference between struct and typedef struct in C++,C 和 C++ 在这方面存在显着差异,因此该答案并不完全适合关于 C 的问题。 这个问题有一个重复的typedef struct vs struct definitions 也有很好的答案。 OTOH, kernel.org/doc/html/v4.10/process/coding-style.html 告诉我们我们不应该做这样的 typedef。 【参考方案1】:

Linux kernel coding style 第 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 是非常好的类型定义,尽管它们比这里更适合 (d) 类。

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

typedef unsigned long myflags_t;

但如果有明确的原因说明它在某些情况下可能是“unsigned int”,而在其他配置下可能是“unsigned long”,那么请务必使用 typedef。

(c) 当您使用 sparse 逐字地创建 new 类型以进行类型检查时。

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

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

因此,允许使用特定于 Linux 的 'u8/u16/u32/u64' 类型及其与标准类型相同的签名等价物——尽管它们在您自己的新代码中不是强制性的。

在编辑已经使用一个或另一组类型的现有代码时,您应该遵守该代码中的现有选择。

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

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

也许还有其他情况,但规则基本上应该是永远不要使用 typedef,除非你能清楚地匹配其中一个规则。

一般来说,一个指针,或者一个结构可以合理地被直接访问的元素应该从不是一个typedef。

【讨论】:

'不透明性和“访问器功能”本身并不好'。有人可以解释为什么吗?我认为信息隐藏和封装将是一个非常好的主意。 @Yawar 我刚刚阅读了这份文件,并有完全相同的想法。当然,C 不是面向对象的,但抽象仍然是一回事。 @Yawar:我认为重点在于“自身”。假设一个类型应该使用float 坐标来表示一个 3d 点。可以要求想要读取点的 x 值的代码使用访问器函数来执行此操作,并且有时真正抽象的“可读 3d 点”类型可能很有用,但还有很多其他时候需要的是一种类型,它可以做float x,y,z三元组可以做的所有事情,并且具有相同的语义。在后一种情况下,尝试使类型不透明会导致混淆而不是清晰。【参考方案2】:

让我们从基础开始,一步步往上走。

这是一个结构定义的例子:

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。如果您打算稍后在您的程序中使用相同的蓝图创建新的结构(结构是一种自定义数据类型),在其定义期间使用typedef 可能是一个好主意,因为您可以节省一些向前的输入。

typedef struct point
  
    int x, y;
   Points;

Points first_point, second_point;

命名自定义类型时要注意

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

【讨论】:

typedef point ... Points 不是一个好主意。由于名称pointPoints 差别太大。当有人必须为指针转发声明struct 时,不同的名称是一个问题,尤其是当它位于可能更改的库中时。如果您使用typedef,请使用相同的名称或有明确的规则如何将名称从struct 名称转换为typedef 名称。【参考方案3】:

正如 Greg Hewgill 所说,typedef 意味着您不再需要到处写struct。这不仅节省了击键次数,还可以使代码更简洁,因为它提供了更多的抽象。

类似的东西

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;

在后一种情况下,您不能按值返回 Point,因为它的定义对头文件的用户是隐藏的。例如,这是一种在GTK+ 中广泛使用的技术。

更新请注意,还有一些备受推崇的 C 项目,其中使用 typedef 隐藏 struct 被认为是一个坏主意,Linux 内核可能是最著名的此类项目。参见The Linux Kernel CodingStyle document 的第 5 章,了解 Linus 的愤怒言论。 :) 我的意思是,问题中的“应该”毕竟不是一成不变的。

【讨论】:

您不应该使用带有下划线后跟大写字母的标识符,它们是保留的(参见第 7.1.3 节第 1 段)。虽然不太可能成为大问题,但在您使用它们时,它在技术上是未定义的行为(7.1.3 第 2 段)。 @dreamlax:如果其他人不清楚,那只是开始带有下划线和大写的标识符,你不应该这样做;您可以在标识符中间随意使用它。 有趣的是,这里给出的示例(其中 typedef 阻止在“所有地方”使用“struct”)实际上比没有 typedef 的相同代码长,因为它恰好节省了一次使用“结构”一词。获得的少量抽象很少与额外的混淆相比更有利。 @Rerito fyi,C99 draft 第 166 页,以下划线和大写字母或另一个下划线开头的所有标识符始终保留用于任何用途。 和 所有以下划线开头的标识符始终保留用作普通和标记名称空间中具有文件范围的标识符。 有趣的是,Linux 内核编码指南说我们应该更加保守地使用 typedefs(第 5 节):kernel.org/doc/Documentation/CodingStyle【参考方案4】:

我认为 typedef 甚至不可能进行前向声明。当依赖项(知道)是双向的时,使用 struct、enum 和 union 允许转发声明。

风格: 在 C++ 中使用 typedef 很有意义。在处理需要多个和/或可变参数的模板时,它几乎是必要的。 typedef 有助于保持命名的正确性。

在 C 编程语言中并非如此。 typedef 的使用通常没有任何目的,只是混淆了数据结构的使用。由于只有 struct (6), enum (4), union (5) 次击键用于声明数据类型,因此几乎没有使用结构的别名。该数据类型是联合还是结构?使用简单的非类型定义声明可以让您立即知道它是什么类型。

请注意 Linux 是如何在严格避免 typedef 带来的这种别名废话的情况下编写的。结果是简约而干净的风格。

【讨论】:

Clean 不会重复structeverywhere... Typedef 会创建新类型。你用什么?类型。我们不关心它是结构体、联合体还是枚举,这就是我们对它进行 typedef 的原因。 不,我们确实关心它是结构还是联合,而不是枚举或某种原子类型。您不能将结构强制为整数或指针(或任何其他类型,就此而言),这是您有时必须存储一些上下文的全部内容。使用“struct”或“union”关键字可以提高推理的局部性。没有人说你需要知道结构 inside 是什么。 @BerndJendrissek:结构体和联合体与其他类型不同,但客户端代码是否应该关心这两个东​​西(结构体或联合体)中的哪一个,比如FILE @supercat FILE 很好地使用了 typedef。我认为 typedef 是过度使用,并不是说它是语言的错误特征。恕我直言,对所有内容都使用 typedef 是“推测性过度笼统”的代码气味。请注意,您将变量声明为 FILE *foo,而不是 FILE foo。对我来说,这很重要。 @supercat: "如果文件识别变量的类型是 FILE 而不是 FILE* ..." 但这正是 typedef 启用的歧义!我们只是习惯于 fopen 获取一个 FILE *,所以我们不担心它,但是每次添加 typedef 时,您都会引入另外一点认知开销:这个 API 需要 foo_t args 还是 foo_t *?显式携带“结构”可以提高推理的局部性,但代价是每个函数定义会增加几个字符。【参考方案5】:

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

typedef struct Tag
...members...
Type;

定义了两件事。标签命名空间中的一个 Tag 和类型命名空间中的一个 Type。所以你可以同时做Type myTypestruct Tag myTagType。像 struct Type myTypeTag myTagType 这样的声明是非法的。此外,在这样的声明中:

typedef Type *Type_ptr;

我们定义了一个指向我们类型的指针。所以如果我们声明:

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

那么 var1,var2myTagType1 是指向类型的指针,但 myTagType2 不是。

在上面提到的书中,它提到 typedefing struct 并不是很有用,因为它只是使程序员免于编写单词 struct。但是,我有一个反对意见,就像许多其他 C 程序员一样。尽管它有时会混淆某些名称(这就是为什么在像内核这样的大型代码库中不建议使用它的原因),但当您想在 C 中实现多态性时,它会很有帮助look here for details。示例:

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;
...

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

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

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

然后你可以声明

unsigned X x; but not
unsigned Y y;

我们并不真正关心结构的这一点,因为它不适用于存储说明符(volatileconst)。

【讨论】:

MyPipe *s; MyWriter *self = (MyWriter *) s; 你刚刚打破了严格的别名。 @JonathonReinhart 可以说明一下如何避免这种情况,例如,演员阵容非常高兴的 GTK+ 如何解决这个问题:bugzilla.gnome.org/show_bug.cgi?id=140722 / mail.gnome.org/archives/gtk-devel-list/2004-April/msg00196.html "typedef 为一个类型引入了一个别名并将其定位在标签命名空间中。即"typedef struct Tag ...members... Type;"定义了两件事"不太有意义。如果 typedef 定义了标签,那么这里的“类型”也应该是一个标签。事实是定义定义了 2 个标签和 1 个类型(或 2 个类型和 1 个标签。不确定):struct TagTagTypestruct Tag 绝对是一种类型。 Tag 是一个标签。但令人困惑的是Type 是标签还是类型【参考方案6】:

在“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 'something' 'name'.'typedef' 将简单地重命名该类型,并且我可以在每次需要时在我的程序中使用这个新名称。所以我们的代码是:

typedef struct complexint 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”为其命名。

【讨论】:

【参考方案7】:

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

B> 结构定义类型。 Structs 允许对一组 var 进行方便的分组,以便于将(K&R、pg127、C prog lang.)作为单个单元进行处理

C> typedef'ing a struct在上面的A中解释过。

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

【讨论】:

【参考方案8】:

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


C 语言命名规则 结构有点古怪,但是 它们非常无害。然而,当 扩展到 C++ 中的类,这些相同 规则为错误打开了小裂缝 爬过去。

在 C 中,名字 s 出现在

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 类型的变量 s。有可能 不是好习惯,但 C 编译器 必须接受它。我从未见过 为什么 C 被设计成这个的理由 大大地。我一直认为这是一个 错了,但就是这样。

许多程序员(包括你的 真的)更喜欢考虑结构名称 作为类型名称,因此它们定义了一个别名 对于使用 typedef 的标签。为了 例如,定义

struct s
    
    ...
    ;
typedef struct s S;

允许您使用 S 代替 struct s, 如

S x;
S *p;

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

S S;    // error

这很好。

结构、联合或 枚举定义是可选的。许多 程序员折叠结构定义 进入 typedef 并省去 完全标记,如:

typedef struct
    
    ...
     S;

链接的文章还讨论了不需要typedef 的 C++ 行为如何导致微妙的名称隐藏问题。为了防止这些问题,最好在 C++ 中 typedef 你的类和结构,即使乍一看似乎没有必要。在 C++ 中,typedef 名称隐藏成为编译器告诉您的错误,而不是潜在问题的隐藏来源。

【讨论】:

标签名称与非标签名称相同的一个示例是在(POSIX 或 Unix)程序中使用 int stat(const char *restrict path, struct stat *restrict buf) 函数。在普通名称空间中有一个函数stat,在标签名称空间中有一个struct stat 你的声明,S S; // 错误 .... 错误 它运行良好。我的意思是你的说法“我们不能为 typedef 标签和 var 使用相同的名称”是错误的......请检查【参考方案9】:

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

我并不是说如果要求发生变化,他们应该改变(可能有两个 C 选项),我们这些在网站上学习面试的人将是 SOL。

【讨论】:

“在 C99 中证明 typedef 是必需的。”什么意思? 问题是关于C,而不是C++。在C 中,typedef 是“必需的”(并且很可能总是如此)。 '必需',您将无法声明变量Point varName;,并且类型与struct Point; 同义,而没有typedef struct Point Point;【参考方案10】:

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;

这到底是什么意思?

【讨论】:

如果 C 标准总是允许在新旧定义精确匹配的情况下重复定义类型名称,我会倾向于让将使用结构类型的代码包含 typedef struct name name;作为一种常见的做法,但是已经证明自己可靠的旧编译器不允许这种重复定义,并且解决该限制会产生比简单地使用 struct tagName 作为类型名称更多的问题。【参考方案11】:

总是 typedef 枚举和结构的另一个很好的理由是由这个问题引起的:

enum EnumDef

  FIRST_ITEM,
  SECOND_ITEM
;

struct StructDef

  enum EnuumDef MyEnum;
  unsigned int MyVar;
 MyStruct;

注意到结构体 (EnuumDef) 中 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 FIRST_ITEM, SECOND_ITEM EnumDef;'没有定义枚举。我已经编写了数百个大型程序,并且不幸地对其他人编写的程序进行维护。根据经验,在结构上使用 typedef 只会导致问题。希望程序员不会因为在声明结构实例时无法键入完整定义而遇到问题。 C不是Basic,所以多输入一些字符不会影响程序的运行。 该示例无法编译,我也不希望它编译。编译 Debug/test.o test.c:10:17: 错误:字段的类型不完整 'enum EnuumDef' enum EnuumDef MyEnum; ^ test.c:10:8: 注意:'enum EnuumDef' enum EnuumDef MyEnum 的前向声明; ^ 1 个错误生成。 gnuc,std=c99。 在 c99 的 clang ang gcc 中,该示例无法编译。但似乎 Visual Studio 并没有抱怨什么。 rextester.com/WDQ5821 @RobertSsupportsMonicaCellio 我认为 typedef 与无 typedef 争论的最大问题之一是它完全发生在 PC 计算的背景下。在嵌入式设备的世界中,C 占主导地位,我们使用一堆既不是 GCC 也不是 clang 的编译器,其中一些可能已经很老了,这可能编译得很好,也可能编译得不好——这就是问题所在。我在嵌入式开发中使用 typedef 结构的典型流程是,每当我要使用 typedef 声明时,我都会查看 typedef 声明,以确保我了解需要什么。我不清楚你们 PC 的人在做什么。【参考方案12】:

令人惊讶的是有多少人弄错了。请不要在 C 中 typedef 结构,它会不必要地污染全局命名空间,而这通常在大型 C 程序中已经被严重污染。

此外,没有标签名称的 typedef 结构是不必要地在头文件之间强加排序关系的主要原因。

考虑:

#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

有了这样的定义,不使用 typedefs,编译单元可以包含 foo.h 来获得 FOO_DEF 定义。如果它不尝试取消引用 foo 结构的“bar”成员,则不需要包含“bar.h”文件。

另外,由于标签名和成员名的命名空间不同,所以可以写出可读性很强的代码,例如:

struct foo *foo;

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

由于命名空间是独立的,因此在命名变量时与其结构标签名称一致不会发生冲突。

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

【讨论】:

更神奇的是,在这个答案给出13个月后,我是第一个点赞的! typedef'ing structs 是 C 的最大滥用之一,在编写良好的代码中没有位置。 typedef 对于去混淆复杂的函数指针类型很有用,实际上没有其他用处。 Peter van der Linden 在他的启发性著作“Expert C Programming - Deep C Secrets”中也提出了反对类型定义结构的案例。要点是:您想知道某物是结构或联合,而不是隐藏它。 Linux 内核编码风格明确禁止类型定义结构。第 5 章:Typedef:“将 typedef 用于结构和指针是一个错误。” kernel.org/doc/Documentation/CodingStyle 一遍又一遍地输入“struct”究竟有什么好处?说到污染,为什么要在全局命名空间中拥有同名的结构和函数/变量/typedef(除非它是同一个函数的typedef)?安全模式是使用typedef struct X ... X。这样,您可以在定义可用的任何地方使用短格式 X 来寻址结构,但仍然可以在需要时前向声明和使用 struct X 我个人很少使用 typedef,我不会说其他人不应该使用它,这不是我的风格。我喜欢在变量类型之前看到一个结构,所以我马上就知道它是一个结构。更容易键入的参数有点蹩脚,具有单个字母的变量也更容易键入,还有自动完成功能现在在任何地方键入 struct 有多难。【参考方案13】:

在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;

因此,在编译时它在堆栈上演变为: 乙: 智力 诠释我 诠释j

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

typedef 是类型说明符,这意味着只有 C 编译器处理它,它可以按照他想要的方式优化汇编代码实现。它也不会像预处理器对结构一样愚蠢地消耗 par 类型的成员,而是使用更复杂的引用构造算法,因此构造如下:

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

typedef struct a //Implemented declaration

    A* b; // member declaration
A;

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

这意味着在 C 中 typedef 更接近于 C++ 类而不是单独的结构。

【讨论】:

拥有自引用结构一点也不难。结构 foo 结构 foo *next;诠释事物; ...什么?用 preprocessor 来描述 structstypedefs 的分辨率已经够糟糕的了,但是你的其余部分内容非常混乱,我发现很难从中得到任何信息。不过,我可以说的一件事是,您认为非typedefd struct 不能被前向声明或用作不透明(指针)成员的想法是完全错误的。在您的第一个示例中,struct b 可以简单地包含struct a *,不需要typedef。断言structs 只是宏观扩张的愚蠢片段,而typedefs 赋予他们革命性的新力量,这是非常错误的【参考方案14】:

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

GTK+ 除了,我不确定标记名是否像结构类型的 typedef 一样普遍使用,所以在 C++ 中可以识别,您可以省略 struct 关键字并将标记名也用作类型名称:

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

【讨论】:

【参考方案15】:

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

struct elem

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

【讨论】:

好的,我们在 C++ 中没有这个问题。那么为什么没有人从 C 的编译器中删除该故障并使其与 C++ 中的相同。好吧,C++ 有一些不同的应用领域,因此它具有高级特性。但是我们不能在不改变 C 的情况下继承其中的一些特性吗?原 C? Manoj,当您需要定义一个引用自身的结构时,标签名称(“struct foo”)是必需的。例如链表中的“下一个”指针。更重要的是,编译器实现了标准,这就是标准所说的。 这不是 C 编译器的故障,而是设计的一部分。他们为 C++ 改变了这一点,我认为这让事情变得更容易,但这并不意味着 C 的行为是错误的。 不幸的是,许多“程序员”定义了一个结构,然后用一些“不相关”的名称对其进行 typedef(如 struct myStruct ...; typedef struct myStruct susan*; 在几乎所有情况下,typedef 只会导致混乱代码,隐藏变量/参数的实际定义,误导所有人,包括代码的原始作者。 @user3629249 我同意你的观点,上面提到的编程风格很糟糕,但这并不是贬低typedefing 结构的理由。你也可以typedef struct foo foo;。当然,struct 关键字不再是必需的,这可能是一个有用的提示,我们看到的类型别名是结构的别名,但总的来说它还不错。还要考虑一种情况,结果类型别名 typedef 的标识符表明它是结构的别名,例如:typedef struct foo foo_struct;

以上是关于为啥我们要在 C 中如此频繁地对结构进行 typedef 呢?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Azure Web 角色上的 IIS 需要如此频繁地重新编译 ASP.NET 应用程序?

为啥loop之后就可以子线程更新ui

为啥 STL 优先级队列错误地对我的类对象进行排序

C# stringbuilder为啥高效

为啥要在linux/unix下进行c/c++编程?

当 asm.js 比普通的 JS 代码快时,我为啥要在 JS 中编写新代码?