typedef struct vs struct定义[重复]
Posted
技术标签:
【中文标题】typedef struct vs struct定义[重复]【英文标题】:typedef struct vs struct definitions [duplicate] 【发布时间】:2010-12-13 02:51:38 【问题描述】:我是 C 编程的初学者,但我想知道在定义结构时使用 typedef
与不使用 typedef
有什么区别。在我看来真的没有区别,他们实现了相同的目标。
struct myStruct
int one;
int two;
;
对比
typedef struct
int one;
int two;
myStruct;
【问题讨论】:
我刚刚在 SO 上读到,第二个选项会导致编译器错误?! “传递不兼容指针类型的参数”***.com/questions/12708897/… 可以找到更好的答案(在我看来)here。 【参考方案1】:struct
和 typedef
是两个截然不同的东西。
struct
关键字用于定义或引用结构类型。例如,这个:
struct foo
int n;
;
创建一个名为struct foo
的新类型。名称foo
是一个标签;只有紧跟在struct
关键字前面时它才有意义,因为标签和其他标识符位于不同的名称空间中。 (这类似于 namespace
s 的 C++ 概念,但限制更多。)
typedef
,尽管有名字,但没有定义新类型;它只是为现有类型创建一个新名称。例如,给定:
typedef int my_int;
my_int
是int
的新名称; my_int
和 int
完全是同一类型。同样,给定上面struct
的定义,你可以这样写:
typedef struct foo foo;
该类型已有名称struct foo
。 typedef
声明为同一类型赋予了一个新名称,foo
。
语法允许您将struct
和typedef
组合成一个声明:
typedef struct bar
int n;
bar;
这是一个常见的成语。现在您可以将这种结构类型称为struct bar
或就像bar
。
请注意,typedef 名称直到声明结束时才可见。如果结构包含指向自身的指针,您可以使用struct
版本来引用它:
typedef struct node
int data;
struct node *next; /* can't use just "node *next" here */
node;
一些程序员会为 struct 标签和 typedef 名称使用不同的标识符。在我看来,没有充分的理由。使用相同的名称是完全合法的,并且更清楚地表明它们是同一类型。如果必须使用不同的标识符,至少要使用一致的约定:
typedef struct node_s
/* ... */
node;
(就我个人而言,我更喜欢省略typedef
并将类型称为struct bar
。typedef
节省了一点打字,但它隐藏了它是结构类型的事实。如果你希望类型不透明,这可能是件好事。如果客户端代码将通过名称引用成员n
,那么它不是不透明的;它显然是一个结构,在我看来,将其称为一个结构是有意义的结构。但是很多聪明的程序员在这一点上不同意我的观点。准备好阅读和理解任何一种方式编写的代码。)
(C++ 有不同的规则。给定 struct blah
的声明,即使没有 typedef,您也可以将类型称为 blah
。使用 typedef 可能会使您的 C 代码更像 C++ --如果你认为这是一件好事。)
【讨论】:
这个答案帮助我更好地理解了为什么 C89 库和 Linux 内核更有可能使用struct mystruct_t
而不是 typedef struct mystruct_t
。
@KeithThompson 好答案 +1。我个人想知道在使用 typedef 时为什么或为什么不省略结构标记,就像 OP 在他的问题中所做的那样:typedef struct int one; int two; myStruct;
。有人说这是因为前向声明,但在这个comment 另一个用户说这也可以使用 typedefs。有理由不省略吗?
还有一件事:你不是说“使用 typedef 可能会使你的 C++ 代码更像 C”而不是“使用 typedef 可能让你的 C 代码更像 C++"?
@RobertSsupportsMonicaCellio 您不能在没有标签的情况下进行前向声明。链接注释中的示例使用 typedef 和 标记。不,我的意思是使用 typedef 可以使 C 代码更像 C++。在 C++ 中,结构(以及类、联合和枚举)使用作为单个标识符的名称来引用。将类型称为 struct foo
更像 C。
很好的答案,真的明白这一点。我想我会以这种方式使用它“struct node .. ;”然后最终更短地键入“typedef struct node node;”在实际使用该结构之前。【参考方案2】:
常见的用法是两者都用:
typedef struct S
int x;
S;
它们是不同的定义。为了让讨论更清楚,我将句子拆分:
struct S
int x;
;
typedef struct S S;
在第一行中,您在结构名称空间中定义标识符 S
(不是 C++ 意义上的)。您可以使用它并通过将参数的类型定义为struct S
来定义新定义类型的变量或函数参数:
void f( struct S argument ); // struct is required here
第二行在全局名称空间中添加了一个类型别名S
,因此您可以只写:
void f( S argument ); // struct keyword no longer needed
请注意,由于两个标识符名称空间不同,因此在结构和全局空间中定义 S
不是错误,因为它不是重新定义相同的标识符,而是在不同的位置创建不同的标识符。
为了让区别更清楚:
typedef struct S
int x;
T;
void S() // correct
//void T() // error: symbol T already defined as an alias to 'struct S'
您可以定义一个与结构同名的函数,因为标识符保存在不同的空间中,但您不能定义一个与 typedef
同名的函数,因为这些标识符会发生冲突。
在 C++ 中,它略有不同,因为定位符号的规则发生了微妙的变化。 C++ 仍然保留了两个不同的标识符空间,但与 C 不同的是,当您只在类标识符空间内定义符号时,您不需要提供 struct/class 关键字:
// C++
struct S
int x;
; // S defined as a class
void f( S a ); // correct: struct is optional
更改的是搜索规则,而不是定义标识符的位置。编译器将搜索全局标识符表,在没有找到S
后,它将在类标识符中搜索S
。
前面给出的代码的行为方式相同:
typedef struct S
int x;
T;
void S() // correct [*]
//void T() // error: symbol T already defined as an alias to 'struct S'
在第二行定义S
函数之后,结构S
不能被编译器自动解析,并且要创建对象或定义该类型的参数,您必须回退到包含@ 987654338@关键字:
// previous code here...
int main()
S();
struct S s;
【讨论】:
很好的答案也向我展示了为什么我想要 typedef,所以它不能作为函数被覆盖,谢谢 :) @AlexanderVarwijk:您想要typedef
以避免需要使用struct
或enum
进行限定。如果您使用的命名约定允许函数和类型使用相同的名称,那么您能做的最好的事情就是检查您如何命名程序的元素。
@panzi:我想不出有什么缺点,相反,如果 type 和 typename 具有相同的名称,对于其他用户来说可能更有意义。
这个答案解释了编译器是如何工作的,使用typedef
是一个好主意——但是,它没有解释为什么在使用typedef
时应该给struct
命名-唯一的声明形式(问题中的第二个示例)。这也是我在断断续续的 C/C++ 编程约 15 年中一直不知所措的地方,也是我所学到的最重要的信息:“总是使用@987654346 @;不使用它是一件很痛苦的事”.
@SlippD.Thompson 在标签命名空间中为struct
命名的原因是它可以被前向声明。 typedef
-仅创建匿名 struct
的别名,并且不能前向声明。 ***.com/a/612350/2757035【参考方案3】:
在 C 中,结构、联合和枚举的类型说明符关键字是强制性的,即您必须始终在类型名称(其标签)前加上 struct
、union
或 @987654326 @ 引用类型时。
您可以使用typedef
去掉关键字,这是一种信息隐藏形式,因为在声明对象时将不再显示对象的实际类型。
因此建议(参见例如Linux kernel coding style guide,第 5 章)仅在以下情况下执行此操作 您实际上想要隐藏此信息,而不仅仅是节省几次击键。
你应该使用typedef
的一个例子是不透明类型,它只与相应的访问器函数/宏一起使用。
【讨论】:
Linux内核编码风格指南供参考:kernel.org/doc/Documentation/CodingStyle Linux内核编码风格文档已移至kernel.org/doc/Documentation/process/coding-style.rst【参考方案4】:如果你使用struct
而不使用typedef
,你总是需要写
struct mystruct myvar;
写是违法的
mystruct myvar;
如果您使用typedef
,则不再需要struct
前缀。
【讨论】:
我的答案不再是最新的了。当前的编译器将“typedef struct”和“struct”视为相同。两者都可以在没有“结构”前缀的情况下引用。即VC2013 的行为就是这样。 感谢您的简短回答以及其他接受。为了秩序,你对“当前编译器”意味着什么?你指的是什么类型的C版本?例如,我在使用 ARM 编译器 (C99) 进行交叉编译期间测试了该行为。在我的情况下,“typedef struct”和“struct”不一样对待广告。 VC 2017 及更高版本(也可能更早)在实例化对象时不需要编写结构...。更多我说不出来。 @REDSOFTADAIR “当前编译器对“typedef struct
”和“struct
”的处理方式相同。两者都可以在没有“struc
”前缀的情况下引用。” -请注意,此语句不适用于 C,它仅适用于 C++,与特定实现的作用无关。 godbolt.org/z/XzFFv6【参考方案5】:
以下代码创建一个别名为 myStruct
的匿名结构:
typedef struct
int one;
int two;
myStruct;
你不能在没有别名的情况下引用它,因为你没有为结构指定标识符。
【讨论】:
【参考方案6】:您不能对typedef struct
使用前向声明。
struct
本身是一个匿名类型,因此您没有要转发声明的实际名称。
typedef struct
int one;
int two;
myStruct;
这样的前向声明是行不通的:
struct myStruct; //forward declaration fails
void blah(myStruct* pStruct);
//error C2371: 'myStruct' : redefinition; different basic types
【讨论】:
【参考方案7】:我看到一些澄清是为了解决这个问题。 C 和 C++ 对类型的定义不同。 C++ 最初只不过是 C 之上的一组附加包含。
如今几乎所有 C/C++ 开发人员都面临的问题是 a) 大学不再教授基础知识,b) 人们不理解定义和声明之间的区别。
此类声明和定义存在的唯一原因是链接器可以计算结构中字段的地址偏移量。这就是为什么大多数人能够摆脱实际上编写不正确的代码的原因——因为编译器能够确定寻址。当有人试图提前做某事时,就会出现问题,例如队列、链表或捎带 O/S 结构。
声明以“struct”开头,定义以“typedef”开头。
此外,结构具有前向声明标签和已定义标签。大多数人不知道这一点,将前向声明标签用作定义标签。
错误:
struct myStruct
int field_1;
...
;
他们刚刚使用前向声明来标记结构——所以现在编译器已经意识到了——但它不是实际定义的类型。编译器可以计算寻址 - 但这不是它的预期用途,原因我将立即说明。
使用这种声明形式的人,必须在几乎每一个对它的引用中都加上“struct”——因为它不是一个正式的新类型。
相反,任何不引用自身的结构都应仅以这种方式声明和定义:
typedef struct
field_1;
...
myStruct;
现在它是一个实际类型,使用时您可以将 at 用作“myStruct”,而无需在其前面加上“struct”一词。
如果你想要一个指向那个结构的指针变量,那么包括一个二级标签:
typedef struct
field_1;
...
myStruct,*myStructP;
现在你有一个指向该结构的指针变量,自定义它。
转发声明--
现在,这里是花哨的东西,前向声明是如何工作的。如果要创建引用自身的类型,例如链表或队列元素,则必须使用前向声明。编译器直到到达最后的分号才考虑定义的结构,因此它只是在该点之前声明。
typedef struct myStructElement
myStructElement* nextSE;
field_1;
...
myStruct;
现在,编译器知道虽然它还不知道整个类型是什么,但它仍然可以使用前向引用来引用它。
请正确声明和定义您的结构。其实是有原因的。
【讨论】:
我不相信 C++ 曾经是“C 之上的一组包含”。第一个实现,cfront 是将 C++ 源代码翻译成 C 源代码的预处理器。我不同意您关于将 typedef 用于结构,尤其是指针的建议。将指针类型隐藏在 typedef 后面可能很危险;恕我直言,最好通过使用*
来明确指出它是一个指针。在您的最后一个示例中,我很好奇使用一个名称(myStruct
)作为 typedef 并使用另一个名称(myStructElement
)作为结构标记的基本原理。
这很大程度上与风格偏好有关。我非常不同意你的大部分建议(这并不是说你错了)。请参阅my answer 了解我自己的偏好(许多,但肯定不是所有 C 程序员都共享)。【参考方案8】:
另一个没有指出的区别是,给结构命名(即结构 myStruct)也使您能够提供结构的前向声明。所以在其他文件中,你可以写:
struct myStruct;
void doit(struct myStruct *ptr);
无需访问定义。我建议您将两个示例结合起来:
typedef struct myStruct
int one;
int two;
myStruct;
这为您提供了更简洁的 typedef 名称的便利,但仍允许您在需要时使用完整的结构名称。
【讨论】:
有一些方法可以使用 typedef 进行前向声明:typedef struct myStruct myStruct;
;然后(稍后):struct myStruct ... ;
。您可以将其用作 either struct myStruct
或 myStruct
之后 typedef
(但在该定义之前类型不完整)。
还值得一提的是,尽管(概念上)自动类型定义标签并将struct SomeThing
和Something
放在单个“符号空间”中,C++ 确实明确允许使用手册typedef SomeThing
重新定义为 struct SomeThing
... 否则您可能会认为这会产生有关名称冲突的错误。来源:***.com/a/22386307/2757035 我猜这是出于(有点徒劳!)向后兼容的原因。
@underscore_d:当时,人们认识到拥有与 C 和 C++ 一样好的代码的价值,并希望避免对兼容性造成不必要的障碍。可悲的是,这种想法已不再流行。【参考方案9】:
typedef
与其他构造一样,用于为数据类型赋予新名称。在这种情况下,它主要是为了使代码更干净:
struct myStruct blah;
对比
myStruct blah;
【讨论】:
【参考方案10】:在后一个示例中,您在使用结构时省略了 struct 关键字。所以在你的代码的任何地方,你都可以写:
myStruct a;
而不是
struct myStruct a;
这节省了一些打字,并且可能更具可读性,但这是一个品味问题
【讨论】:
【参考方案11】:当您使用struct
时,差异就出现了。
你必须做的第一种方式:
struct myStruct aName;
第二种方式允许您删除关键字struct
。
myStruct aName;
【讨论】:
【参考方案12】:在 C(不是 C++)中,您必须声明结构变量,例如:
struct myStruct myVariable;
为了能够改用myStruct myVariable;
,你可以typedef
这个结构:
typedef struct myStruct someStruct;
someStruct myVariable;
您可以将struct
定义和typedef
s 组合在一个声明匿名struct
和typedef
s 的语句中。
typedef struct ... myStruct;
【讨论】:
最后一段代码与前面的代码不等价。在最后一行中,您将类型别名“myStruct”定义为未命名的结构。两个版本之间存在(非常)细微的差异。 dribeas:我在句子“...一个声明匿名结构和...的单一语句中涵盖了这种细微的差异” @DavidRodríguez-dribeas 想详细说明细微差别吗? @anthony-arnold:嗯……我想我已经提到过了。在一种情况下,有一个类型有一个名称和一个别名,在另一种情况下,你只有一个别名。它在哪里重要?很少,但如果你有一个匿名类型,你不能使用struct T x;
来声明一个变量,或者为不同类型的符号重用名称:typedef struct f; void f(); struct f x;
在最后两个语句中失败。当然,不推荐使用这样的代码(类型和函数同名?)
接受的答案没有很好地对比“struct”和“typedef struct”,这个答案让我清楚地知道使用了“typedef struct”技术,因此 C 可以在为了在传递数据类型时像使用数据类型一样使用结构时省略“struct”。以上是关于typedef struct vs struct定义[重复]的主要内容,如果未能解决你的问题,请参考以下文章
C : typedef 结构名称 ...; VS typedef struct... 名称;
typedef vs struct/union/enum 背后的基本原理是啥,难道不能只有一个命名空间吗?
C语言结构体中struct和typedef struct有啥区别?