![](/img/trans.png)
[英]What's the difference between `typedef struct X { }` and `typedef struct { } X`?
[英]What is the real difference between struct and typedef struct in C?
定义结构体有两种主要方式:
struct triangle_s {
int a,b,c;
};
和
typedef struct triangle_s {
int a,b,c;
} triangle;
已经被问过很多次了,但每个答案都是关于在使用typedef
变体时不必多次编写struct
。 除了可以避免重复struct
关键字之外,还有什么真正的区别吗? 我听说你永远不应该在 C 中使用typedef
变体,但没有人说为什么。
没有区别, typedef
只是删除了使用struct
前缀变量声明的要求。
默认情况下是否使用typedef
结构是一场宗教战争,主要是由手头有太多时间的人进行的。 在现有代码库中工作时,您应该执行编码标准或周围代码所做的任何操作。 对于您自己的个人代码,请随心所欲。
没有“typedef 结构”这样的东西。
struct
类型 struct
引入了一种结构,它是由一组命名成员组成的聚合数据类型。 (数组也是聚合,但它们由许多被索引的相同成员组成。联合有一组成员名称,但一次只能包含一个成员,因此它们不是聚合。您可能不需要我知道。)
结构类型通常有标签,因此实际的类型名称将类似于struct Triangle
。 标签 ( Triangle
) 与标识符不在同一个命名空间中,因此使用也用于其他目的的标签没有问题。 有些人喜欢用_s
和_u
附加标签,分别表示它们是结构或联合标签,但这不是我的风格; 我更喜欢在 CamelCase 中命名类型,对我来说,结构或联合标记代表类型名。 但是当然您可以自由使用自己的约定。
如果您在程序中使用struct SomeTag
,您实际上是在声明存在一个其标记为SomeTag
的结构。 您不需要通过命名或描述结构的成员来填写声明,除非您需要在代码中引用它们。 其成员尚未(尚未)声明的结构称为不完整的,但它仍然可以用作指针类型的一部分,因为 C 标准保证所有结构指针都具有相同的格式,而不管结构的内容如何。 (这并不使它们可以互换,但它确实意味着编译器知道指针有多大。)从未定义其成员并且仅用作指针类型的目标的结构称为opaque 。
您可以通过添加成员声明块来完成结构的声明。 所以
struct Triangle {
int a,b,c;
};
首先声明存在一个名称为struct Triangle
,然后通过声明三个都是int
的命名成员来填充该结构的定义。
顺便说一下,联合声明和定义都非常相似。
结构定义可以在声明中使用,就好像它是一个类型名称一样。 或者换句话说,您可以声明结构类型的标记,立即填写字段,然后声明该类型的一个或多个变量:
struct Triangle { int a, b, c; } aTriangle, anotherTriangle;
这不是一种非常常见的风格,但重要的是要知道语法是可能的。
最后,定义一个结构而不给它一个标签是合法的。 无标签结构类型有一个怪癖:通常没有两种结构类型可以具有相同的标签,但所有无标签结构都是不同的。 这意味着您可以声明一个结构类型,它实际上没有名称,并且不同于任何其他结构类型,甚至是具有完全相同成员的结构类型。 如果您有一个只有一个实例(“单例”)的聚合,这可能会稍微有用,尽管我自己不会真正使用这种样式。 但经过一小段弯路后,我们将看到此功能的另一种用途。
C 类型名称可能非常复杂,因为它们可以由多个部分组成。 例如, const struct Triangle*[8]
是一个包含八个成员的数组,每个成员都是一个指向不可修改的struct Triangle
的指针。 double (*)(const struct Triangle*[8])
是一个函数,它接受一个这样的数组作为参数(或者,更准确地说,它接受一个指向这样一个数组的第一个元素的指针,因为数组到指针衰变。但这在这里无关紧要。)
为了使复杂类型更易于使用,C 允许您为类型声明别名。 别名使用typedef
声明,否则看起来与变量声明完全一样。 因此,例如,您可以声明一个int
类型的变量
int someNumber;
因此你可以为int
类型声明一个别名
typedef int someType;
类似地,您可以声明一个包含八个指向const Triangle
元素的指针的数组
const Triangle* eightSlices[8];
以完全相同的方式,您可以为此类数组的类型声明一个名称:
typedef const Triangle* EightSlices[8];
请注意,类型的名称恰好位于对象名称所在的位置,它可以位于声明中间的某个位置。
作为声明类型别名的简单示例,以下是为结构类型声明别名的方法:
一个不完整的结构定义:
typedef struct Triangle Triangle;
或者一个完整的结构定义:
typedef struct Triangle {
int a, b, c;
} Triangle;
或者两者都分开(这些可以按任何顺序进行):
typedef struct Triangle Triangle;
struct Triangle {
int a, b, c;
};
请记住,结构标记和其他标识符(例如类型别名)在不同的命名空间中,因此上面Triangle
的两种用法没有冲突。 一些程序员觉得有必要区分它们,即使其中一个只能紧跟在struct
之后使用,另一个不能在struct
。 其他人——我想你可以猜到我属于这群人——发现故意为两者使用相同的名称很方便,依靠单词struct
的存在或不存在来让我们知道它是类型别名还是一个标签。 (而且,更常见的是,表明我们无意再次使用该标签。)
所以,回到我的开场白:
没有“typedef 结构”这样的东西。
而且没有。 我们这里有一个非常普通的struct
,声明和定义。 我们有一个非常普通的类型别名,它为该struct
提供了一个替代名称。 就是这样。
请注意,您可以为匿名类型(例如无标记结构类型)提供别名,之后该类型就不再是匿名的。 所以有些人会在上面的定义中省略标签:
typedef struct {
int a, b, c;
} Triangle;
这看起来很像上面提到的单例结构类型,但由于它是一种可以用于声明多个实例的类型。 但我实际上并不推荐这种风格。
每个人都有自己的风格偏好,而这些偏好大多是有效的。 我们大多数人都参与过不止一个项目,而且由于每个项目都有自己的风格指南,我们需要学习如何在不同的项目中接受和使用不同的风格。 但是我认为我们中的大多数人都有一些我们觉得最舒服的风格,当我们为自己编写代码时(或者当我们开始一个项目以吸引准备符合我们的同事的意图时,我们会恢复这种风格)风格)。 我上面使用的是我的风格。
事实上,我尽量避免使用压缩声明+定义+别名的语法,更喜欢上面显示的两个声明版本:
typedef struct Triangle Triangle; /* type alias */
struct Triangle {
int a, b, c;
}; /* type definition */
我更喜欢这样做的原因是它允许我使用自引用成员定义类型,例如链表和树:
typedef struct TriangleList TriangleList;
struct TriangleList {
Triangle slice;
TriangleList* next;
};
(如果我没有对类型进行前向别名化,我将不得不将成员声明为struct TriangleList* next;
这会导致更难看的对齐。)
有时我最终会得到相互引用的类型,在这种情况下,我需要在任何结构定义之前将别名收集在一起。 这也可能是有利的,因为别名定义允许不透明地使用指向类型的指针,因此可以放置到根本不包含类型定义的公共标头中。
但这只是我。 随意忽略它。
定义结构体有两种主要方式
不,只有一个:
struct triangle_s {
int a,b,c;
}
. 这样的定义可以出现在多种上下文中,包括但不限于您提供的两种情况,但重要的是要了解您提供的两种语句都不仅仅是结构定义。
不可否认,这个“更多”有点空洞:
struct triangle_s { int a,b,c; };
它是结构类型和该类型的零对象的声明。 在右大括号和分号之间有一个标识符列表,在这种特殊情况下它恰好是空的。 但也可能是这样,例如:
struct triangle_s {
int a,b,c;
} triangle;
,它定义了类型struct triangle_s
并将triangle
声明为该类型的对象。 当然,这将我们引向第二个例子
typedef struct triangle_s { int a,b,c; } triangle;
,这与前面的完全相同,除了typedef
关键字指示声明的标识符是类型别名而不是对象标识符。 同样,结构定义只是从struct
关键字到关联的右大括号的部分,请注意typedef
可用于声明任何类型的别名,而不仅仅是结构。
除了可以避免重复 struct 关键字之外,还有什么真正的区别吗?
无论哪种方式,您都有struct triangle_s
的定义,并且您可以以任何一种方式使用其类型名称的该形式。 在后一种情况下,你也可以用triangle
(无论它是在范围内)作为一个别名struct triangle_s
,对于任何和一切可以由形式送达目的struct triangle_s
,在意义上没有区别。 这就是typedef
。
我听说你永远不应该在 C 中使用 typedef 变体,但没有人说为什么。
有些人从风格上反对第二种形式,因为它在同一个声明中混合了两种不同的东西——结构类型的定义和它的别名声明。 这条推理线继续说,如果你想要typedef
,那么你应该单独声明它:
struct triangle_s {
int a,b,c;
};
typedef struct triangle_s triangle;
然而,这又是一个代码风格问题,而不是功能问题。
关于typedef
的使用,还有其他一些风格点,我个人对此有更强烈的感受。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.