ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

elem user;

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

更彻底和准确的答案:stackoverflow.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。

r
ratchet freak

正如 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 的相同代码要长,因为它恰好保存了单词“struct”的一次使用。获得的一点点抽象很少与额外的混淆相比更有利。
@Rerito fyi,第 166 页,第 C99 draft以下划线和大写字母或另一个下划线开头的所有标识符始终保留用于任何用途。所有以下划线开头的标识符下划线始终保留用作普通和标记名称空间中的文件范围的标识符。
有趣的是,Linux 内核编码指南说我们在使用 typedef 时应该更加保守(第 5 节):kernel.org/doc/Documentation/CodingStyle
J
Jens

令人惊讶的是有多少人犯了这个错误。请不要在 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

使用这样的定义,而不使用 typedef,编译单元可以包含 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 内核编码风格明确禁止 typedefing 结构。第 5 章:Typedef:“将 typedef 用于结构和指针是一个错误。” kernel.org/doc/Documentation/CodingStyle
一遍又一遍地键入“struct”究竟有什么好处?说到污染,为什么要在全局命名空间中拥有同名的结构和函数/变量/typedef(除非它是同一个函数的typedef)?安全模式是使用 typedef struct X { ... } X。这样,您可以在定义可用的任何地方使用缩写形式 X 来寻址结构,但仍然可以在需要时前向声明和使用 struct X
我个人很少使用 typedef,我不会说其他人不应该使用它,这不是我的风格。我喜欢在变量类型之前看到一个结构,所以我马上就知道它是一个结构。更容易键入的参数有点蹩脚,具有单个字母的变量也更容易键入,而且自动完成现在在任何地方键入 struct 有多难。
2
2 revs, 2 users 99%

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

命名结构的 C 语言规则有点古怪,但它们是无害的。然而,当扩展到 C++ 中的类时,这些相同的规则打开了一些小裂缝,让 bug 爬过。在 C 中,名称 s 出现在 struct s { ... };是一个标签。标签名称不是类型名称。给定上面的定义,声明如 sx; /* C 语言错误 */ s *p; /* C 中的错误 */ 是 C 中的错误。您必须将它们写为 struct sx; /* OK */ struct s *p; /* OK */ 联合和枚举的名称也是标签而不是类型。在 C 中,标签不同于所有其他名称(对于函数、类型、变量和枚举常量)。 C 编译器在符号表中维护标记,该符号表在概念上与包含所有其他名称的表在物理上是分开的。因此,C 程序有可能在同一范围内同时拥有一个标记和另一个拼写相同的名称。例如,结构 ss;是一个有效的声明,它声明了 struct s 类型的变量 s。这可能不是好的做法,但 C 编译器必须接受它。我从来没有看到为什么 C 是这样设计的理由。我一直认为这是一个错误,但它就在那里。许多程序员(包括你的程序员)更喜欢将结构名称视为类型名称,因此他们使用 typedef 为标签定义别名。例如,定义 struct s { ... }; typedef struct s S;允许您使用 S 代替 struct s,如在 S x 中; S *p;程序不能同时使用 S 作为类型和变量(或函数或枚举常量)的名称:SS; // 错误 这很好。结构、联合或枚举定义中的标记名称是可选的。许多程序员将结构定义折叠到 typedef 中并完全省去标记,例如: typedef struct { ... } S;

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


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

使用 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 我同意你的观点,提到的编程风格很糟糕,但这并不是诋毁 typedef 结构的理由。你也可以做typedef struct foo foo;。当然,struct 关键字不是必需的,这可能是一个有用的提示,我们看到的类型别名是结构的别名,但总的来说它还不错。还要考虑这样一种情况,即生成的类型别名 typedef 的标识符表明它是结构的别名,fe: typedef struct foo foo_struct;
M
M.M

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

enum EnumDef
{
  FIRST_ITEM,
  SECOND_ITEM
};

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

注意到 struct (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) */

我会提倡 ALWAYS typedef'ing 结构和枚举。

不仅是为了节省一些打字(不是双关语;)),而且因为它更安全。


更糟糕的是,您的拼写错误可能与不同的标签一致。在结构的情况下,这可能导致整个程序正确编译并具有运行时未定义的行为。
这个定义:'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,标准 = c99。
在带有 c99 的 clang ang gcc 中,该示例无法编译。但似乎 Visual Studio 并没有抱怨什么。 rextester.com/WDQ5821
@RobertSsupportsMonicaCellio 我认为 typedef 与无 typedef 争论的最大问题之一是它完全发生在 PC 计算的背景下。在嵌入式设备的世界中,C 占主导地位,我们使用一堆既不是 GCC 也不是 clang 的编译器,其中一些可能已经很老了,这可能编译得很好,也可能编译得不好——这就是问题所在。我在嵌入式开发中使用 typedef 结构的典型流程是,每当我要使用 typedef 声明时,我都会查看它以确保我了解需要什么。我不清楚你们PC家伙做什么。
Y
Yun

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) 类。笔记!再次 - 需要有一个理由。如果某些东西是“unsigned long”,那么就没有理由做 typedef unsigned long myflags_t;但是,如果有明确的理由说明它在某些情况下可能是“unsigned int”,而在其他配置下可能是“unsigned long”,那么一定要继续使用 typedef。 (c) 当您使用 sparse 从字面上创建一个新类型进行类型检查时。 (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 三元组可以做的所有事情的类型,并且具有相同的语义。在后一种情况下,试图使类型不透明会导致混乱而不是清晰。
S
SridharKritha

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

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

定义了两件事。 1)标签命名空间中的标签和2)类型命名空间中的类型。因此,您可以同时执行 Type myTypestruct Tag myTagTypestruct Type myTypeTag myTagType 之类的声明是非法的。此外,在这样的声明中:

typedef Type *Type_ptr;

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

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

那么 var1var2myTagType1 是指向 Type 但 myTagType2 不是的指针。

在上面提到的书中,它提到 typedefing structs 不是很有用,因为它只是使程序员免于编写单词 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 是标签还是类型
Y
Yoon5oo

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

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

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

请注意,Linux 是如何在严格避免 typedef 带来的这种别名胡说八道的情况下编写的。结果是简约和干净的风格。


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

让我们从基础开始,逐步向上。

这是结构定义的示例:

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 名称。
p
philsquared

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

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


    struct MyStruct
    {
      int i;
    };

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

n
natersoz

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 作为类型名称更多的问题。
J
JamesAD-0

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

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

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

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


M
Matthew Corey Brown

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

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


“原来在 C99 typedef 中是必需的。”你是什么意思?
问题是关于 C,而不是 C++。在 C 中,typedef 是“必需的”(并且很可能总是如此)。 '必需',您将无法声明变量 Point varName; 并且类型与没有 typedef struct Point Point;struct Point; 同义。
Y
Yoon5oo

在“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 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”给它一个名称。


d
doccpu

根本上,在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 编译器处理它,它可以像他想要的那样优化汇编代码实现。它也不会像预处理器对结构一样愚蠢地消耗 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 来描述 structstypedef 的分辨率已经够糟糕的了,但是你的其余部分内容非常混乱,我发现很难从中得到任何信息。不过,我可以说的一件事是,您认为非 typedefd struct 不能被向前声明或用作不透明(指针)成员的想法是完全错误的。在您的第一个示例中,struct b 可以简单地包含一个 struct a *,不需要 typedefstructs 只是宏观扩张的愚蠢片段,typedefs 赋予它们革命性的新力量的断言是非常错误的