我是C编程的初学者,但我想知道在定义结构时使用typedef与不使用typedef有什么区别。在我看来,似乎没有什么区别,他们实现了相同的目标。
1 2 3 4
| struct myStruct{
int one;
int two;
}; |
VS
1 2 3 4
| typedef struct{
int one;
int two;
}myStruct; |
- 我只是在这里读到,所以第二个选项会给出一个编译器错误?!"传递不兼容指针类型的参数"stackoverflow.com/questions/12708897/…
常用的成语是同时使用:
1 2 3
| typedef struct X {
int x;
} X; |
它们是不同的定义。为了使讨论更清楚,我将把句子分开:
1 2 3 4 5
| struct S {
int x;
};
typedef struct S S; |
在第一行中,您定义了结构名称空间内的标识符EDCOX1 OR 13(不在C++意义上)。通过将参数类型定义为struct S可以使用它并定义新定义类型的变量或函数参数:
1
| void f( struct S argument ); // struct is required here |
第二行在全局名称空间中添加了一个类型别名S,因此您只需编写:
1
| void f( S argument ); // struct keyword no longer needed |
注意,由于两个标识符名称空间都不同,因此在结构和全局空间中定义S不是一个错误,因为它不是重新定义同一个标识符,而是在不同的位置创建不同的标识符。
为了使区别更清楚:
1 2 3 4 5 6 7
| 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不同,当您只在类标识符空间中定义符号时,不需要提供Stutt/Class关键字:
1 2 3 4 5 6
| // C++
struct S {
int x;
}; // S defined as a class
void f( S a ); // correct: struct is optional |
搜索规则的变化是什么,而不是标识符的定义。编译器将搜索全局标识符表,在找不到S之后,将在类标识符中搜索S。
前面介绍的代码的行为方式相同:
1 2 3 4 5 6 7
| typedef struct S {
int x;
} T;
void S() {} // correct [*]
//void T() {} // error: symbol T already defined as an alias to 'struct S' |
在第二行中定义S函数后,编译器无法自动解析结构,要创建对象或定义该类型的参数,必须返回到包含struct关键字:
1 2 3 4 5
| // previous code here...
int main() {
S();
struct S s;
} |
- 很好的答案也说明了我为什么要typedef,所以它不能被重写为函数,谢谢:)
- @Alexandervarwijk:你想用typedef来避免需要用struct或enum来取得资格。如果使用的命名约定允许使用相同名称的函数和类型,那么最好的做法是检查如何命名程序的元素。
- 我的命名约定应该确保不会发生这种情况,但要比抱歉更安全:)
- 给这个类型取与struct或enum相同的名称有什么缺点吗?例如,如果编写一个lib,想要简洁的类型名,并且不想为基础结构和枚举考虑不同的名称。有些结构可能不是公开定义的(它们只能通过指针访问),并且只有typedefed名称是(将被)文档化的。
- @Panzi:我想不出有什么缺点,相反,如果类型和类型名同名,对其他用户可能更有意义。
- 基本上,使用不同的标识符名称以避免混淆。D
- 这个答案解释了编译器是如何工作的,使用typedef是一个好主意——但是,它并不能解释为什么在使用typedef形式的声明(问题中的第二个例子)时,应该给struct命名。这也是我失去了长达15年的断断续续的C/C++编程的原因,也是我所教过的最重要的信息:"总是使用EDCOX1,2,不使用它是一种痛苦。"
- 枚举也应该以两种方式命名,就像结构一样?
- @slippd.thompson在标记名称空间中给struct指定名称的原因是,可以向前声明它。typedef—只创建匿名struct的别名,不能转发声明。stackoverflow.com/a/612350/2757035
- @在"D"下加下划线。我已经否决了这个答案,并支持RSamuelKlatchko,因为这个原因,这个答案解释了一堆内部没有实际提供安慰;RSAMUEL简洁地回答C++程序员需要知道什么来编写良好的可靠代码。
- SLPPP.THOPPSON:这个问题恰好是C,C++中,你不会提供TyPulf…但是是的,在答案中没有明确提到转发声明的能力,它是隐式的,但是在将别名与实际类型分离时,它可能更显式。
- @davidrodr&237;guez dribeas你的答案中也没有明确提到的是任何通过使用typedef来实现的(人类程序员方面)。相反,您将讨论编译器的符号解析,以及懒惰的程序员可能会遇到的潜在名称冲突陷阱(那些不努力给每个构造一个独特的有意义的名称的人)。我的意思是,这是一个有趣的答案,对核心C++程序员很有帮助,所以不要误解我的意思,这不是一个糟糕的答案。这并不是手术所追求的答案(imho)。当操作人员需要使用时,它是内部的。
- @davidrodr&237;guez dribeas我有权利认为对C来说,创建这个单独的struct名称空间并在任何地方都需要struct关键字是完全愚蠢的,除非您使用typedef struct语句,或者是否有某种我应该知道的原因?因为在我看来,人们大多只是使用typedef struct语句来破坏单独名称间距的需求目的。
- @安迪:我不在那里,我也不知道设计的决定。想到的一件事是,除了typedef名称外,通用标识符空间还包含一些在二进制文件中显示为符号的内容。
- @Slippd.Thompson:"在我看来,似乎没有什么区别,他们实现了同样的目标。"答案试图解决这样一个事实,即存在差异,以及差异所在,包括使之可见的场景,就像没有名称冲突时,没有可感知的差异一样。
- 以下语法是有效的typedef struct{ int x; } T;,然后是T varName;?
struct和typedef是两个非常不同的东西。
struct关键字用于定义或引用结构类型。例如,这:
1 2 3
| struct foo {
int n;
}; |
创建名为struct foo的新类型。名称foo是一个标记;只有当它前面紧跟着struct关键字时,它才有意义,因为标记和其他标识符在不同的名称空间中。(这类似于,但比namespace的C++概念更受限制。
typedef不定义新的类型,它只是为现有的类型创建一个新的名称。例如,给定:
my_int是int的新名称;my_int和int是完全相同的类型。同样,考虑到上面的struct定义,您可以编写:
类型已经有一个名称,struct foo。typedef声明为同一类型提供了一个新名称,foo。
语法允许您将struct和typedef组合成一个声明:
1 2 3
| typedef struct bar {
int n;
} bar; |
这是一个常见的成语。现在您可以将这种结构类型称为struct bar,也可以称为bar。
注意,typedef名称直到声明结束后才可见。如果结构包含指向自身的指针,您可以使用struct版本来引用它:
1 2 3 4
| typedef struct node {
int data;
struct node *next; /* can't use just"node *next" here */
} node; |
一些程序员将对struct标记和typedef名称使用不同的标识符。在我看来,这样做没有什么好的理由;使用同一个名字是完全合法的,而且更清楚地表明它们是同一类型的。如果必须使用不同的标识符,请至少使用一致的约定:
1 2 3
| typedef struct node_s {
/* ... */
} node; |
(就我个人而言,我更喜欢省略typedef,将该类型称为struct bar。typedef保存了一些类型,但它隐藏了一个事实,即它是一种结构类型。如果您希望类型是不透明的,这可能是一件好事。如果客户机代码将按名称引用成员n,那么它不是不透明的;它显然是一个结构,在我看来,将其称为一个结构是有意义的。但是很多聪明的程序员在这一点上不同意我的观点。准备好阅读和理解以任何方式编写的代码。)
(C++有不同的规则。给定struct blah的声明,您可以将该类型称为blah,即使没有typedef。使用TyPulf可以使C代码更像C++——如果你认为这是一件好事。
- 这个答案帮助我更好地理解为什么C89库和Linux内核更倾向于使用struct mystruct_t {},而不是typedef struct {} mystruct_t。
另一个未指出的区别是,为结构命名(即structmystruct)也可以使您提供结构的前向声明。所以在其他文件中,你可以写:
1 2
| struct myStruct;
void doit(struct myStruct *ptr); |
无需访问定义。我建议你结合两个例子:
1 2 3 4
| typedef struct myStruct{
int one;
int two;
} myStruct; |
这为您提供了更简洁的typedef名称的便利性,但仍然允许您根据需要使用完整的结构名称。
- 有一些方法可以使用typedef进行前向声明:typedef struct myStruct myStruct;;然后(稍后):struct myStruct { ... };。在该typedef之后,您可以将其用作struct myStruct或只是myStruct(但在该定义之前,类型是不完整的)。
- 还值得一提的是,C++(尽管在概念上)AutoType Debug标签并在一个"符号空间"中同时放置EDCOX1 13和EDOCX1 14,确实明确地允许手动EDCOX1〔15〕重新定义EDCOX1〔13〕…否则,您可能会认为这会产生关于冲突名称的错误。资料来源:stackoverflow.com/a/22386307/2757035我想这是为了(有些徒劳!)向后兼容性原因。
- 在下面,人们认识到代码的价值,它可以像C和C++一样工作,并且希望避免创建不必要的兼容性障碍。可悲的是,这种想法已不再流行。
在C(非C++)中,必须声明结构变量,例如:
1
| struct myStruct myVariable; |
为了能够使用myStruct myVariable;,您可以使用typedef结构:
1 2
| typedef struct myStruct someStruct;
someStruct myVariable; |
您可以将struct定义和typedef的定义组合在一个声明中,声明匿名struct和typedef的定义。
1
| typedef struct { ... } myStruct; |
- 最后一块代码不等于前一个代码。在最后一行中,您要将类型别名"mystruct"定义为未命名的结构。两个版本之间有(非常)细微的差别。
- Dribeas:我在句子"…声明匿名结构的单个语句和…"中介绍了这种细微的差别。
- 是的,我好像跳过了那部分。)
- @Davidrodr&237;Guez Dribeas是否愿意详细阐述细微差异?
- @安东尼·阿诺德:嗯……我想我已经提到了。在一种情况下,有一个具有名称和别名的类型,而在另一种情况下,您只有一个别名。有什么关系?但是,如果您有一个annonymous类型,则不能执行struct T x;来声明变量,也不能将名称重新用于其他类型的符号:typedef struct {} f; void f(); struct f x;在最后两个语句中失败。当然,不建议使用这样的代码(类型和同名函数?)
- @Davidrodr&237;Guez Dribeas谢谢。
- 谢谢你说的魔法词:annonymous结构。它只在C11标准化。
- 接受的答案并没有很好地对比"Stutt"和"TyBuffFrast",这个答案让我明白了"TyPulfStrut"技术被使用,所以C可以模仿C++来省略"Stutt"。
如果你用struct而不使用typedef的话,你总是要写
写是违法的
如果使用typedef,则不再需要struct前缀。
- 我的答案不再是最新的了。当前编译器将"typedef struct"和"struct"处理相同。两者都可以不使用"struct"前缀引用。也就是说,VC2013就是这样。
- 我很感谢你的简短回答以及对方接受的回答。为了顺序,您对"当前编译器"是什么意思?你指的是什么类型的C版本?例如,我在使用ARM编译器(C99)进行交叉编译期间测试了行为。在我的例子中,"typedef struct"和"struct"不一样。
- VC 2017及更高版本(可能更早)不需要编写结构…在实例化对象时。我说不出更多。
在C语言中,结构、联合和枚举的类型说明符关键字是强制的,即在引用类型时,必须在类型名称(其标记)前面加上struct、union或enum。
通过使用typedef可以去掉关键字,这是一种信息隐藏形式,因为在声明对象时,对象的实际类型将不再可见。
因此,建议(如Linux内核编码风格指南,第5章)仅在您实际上想要隐藏这些信息,而不仅仅是保存一些按键。
一个应该何时使用typedef的示例是一个不透明的类型,它只与相应的访问函数/宏一起使用过。
- 参考Linux内核编码风格指南:kernel.org/doc/documentation/coding style
- Linux内核编码风格文档已移至kernel.org/doc/documentation/process/coding-style.rst
不能对typedef struct使用forward声明。
struct本身是一个匿名类型,因此您没有要转发声明的实际名称。
1 2 3 4
| typedef struct{
int one;
int two;
} myStruct; |
这样的远期申报是行不通的:
1 2 3 4 5
| struct myStruct; //forward declaration fails
void blah(myStruct* pStruct);
//error C2371: 'myStruct' : redefinition; different basic types |
下面的代码创建一个匿名结构,其别名为myStruct:
1 2 3 4
| typedef struct{
int one;
int two;
} myStruct; |
没有别名不能引用它,因为没有为结构指定标识符。
当你使用struct时,差异就出现了。
第一种方法是:
第二种方法允许您删除关键字struct。
与其他构造一样,typedef用于为数据类型提供新名称。在这种情况下,主要是为了使代码更干净:
VS
我看到一些澄清是正确的。C和C++不以不同的方式定义类型。C++最初只不过是C上的一个附加集合。
现在几乎所有的C/C++开发者都有一个问题:A)大学不再教授基础知识,B)人们不理解定义和声明之间的区别。
这种声明和定义存在的唯一原因是链接器可以计算到结构中字段的地址偏移量。这就是为什么大多数人都会忽略那些实际上写得不正确的代码——因为编译器能够确定地址。当有人试图提前做一些事情时,比如队列、链表,或者清管支持O/S结构时,问题就会出现。
声明以"struct"开头,定义以"typedef"开头。
此外,结构有一个正向声明标签和一个定义的标签。大多数人不知道这一点,而是将前向声明标签用作定义标签。
错误:
1 2 3 4 5
| struct myStruct
{
int field_1;
...
}; |
他们刚刚使用了forward声明来标记结构——所以现在编译器意识到了这一点——但它不是一个实际定义的类型。编译器可以计算地址——但这并不是它的预期用途,因为我将马上展示的原因。
使用这种声明形式的人,必须在实践中始终使用"struct",因为它不是正式的新类型。
相反,任何不引用自身的结构都应该以这种方式声明和定义:
1 2 3 4 5
| typedef struct
{
field_1;
...
}myStruct; |
现在它是一个实际类型,使用时,您可以将at用作"mystruct",而不必在其前面加上"struct"一词。
如果需要指向该结构的指针变量,请包含辅助标签:
1 2 3 4 5
| typedef struct
{
field_1;
...
}myStruct,*myStructP; |
现在您有了一个指向该结构的指针变量,它是自定义的。
远期申报--
现在,这里有一些花哨的东西,转发声明是如何工作的。如果要创建引用自身的类型,如链表或队列元素,则必须使用forward声明。编译器不会考虑定义的结构,直到它到达末尾的分号,所以它只是在这一点之前声明的。
1 2 3 4 5 6
| typedef struct myStructElement
{
myStructElement* nextSE;
field_1;
...
}myStruct; |
现在,编译器知道尽管它还不知道整个类型是什么,但它仍然可以使用前向引用引用引用它。
请正确声明和typedef您的结构。其实是有原因的。
- 我不相信C++曾经是"C上的一组包含"。第一个实现,CWAN是一个预处理器,它将C++源代码翻译成C源代码。我不同意你关于在结构中使用typedef的建议,尤其是对于指针。在typedef后面隐藏一个指针类型是危险的;最好使用*将它作为一个指针显式地表示出来。在最后一个示例中,我对使用一个名称(myStruct表示typedef,另一个名称(myStructElement表示struct标记)的原理感到好奇。
- 这在很大程度上是一个风格偏好的问题。我非常不同意你的大多数建议(不是说你错了)。请参阅我的答案,了解我自己的偏好(许多C程序员共享这些偏好,但肯定不是所有C程序员都共享)。
在后一个示例中,使用结构时省略struct关键字。因此,在代码的任何地方,您都可以编写:
而不是
这节省了一些输入,而且可能更易读,但这是一个品味问题。