我是C编程的初学者,但我想知道在定义结构时使用typedef
与不使用typedef
之间有什么区别。在我看来,实际上没有区别,他们实现了同样的目标。
struct myStruct{
int one;
int two;
};
VS
typedef struct{
int one;
int two;
}myStruct;
答案 0 :(得分:895)
常见的习语是同时使用:
typedef struct X {
int x;
} X;
它们的定义不同。为了使讨论更清楚,我将分开句子:
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
函数之后,编译器无法自动解析struct S,并且要创建对象或定义该类型的参数,您必须回退到包含{ {1}}关键字:
struct
答案 1 :(得分:109)
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
可以节省一点点打字,但它隐藏了这样一个事实:#sa结构类型。如果你希望类型是不透明的,这可能是一件好事。如果客户代码将按名称引用成员n
,那么它不是不透明的;它是&#39 ; s显然是一个结构,在我看来将其称为结构是有道理的。但是很多聪明的程序员在这一点上不同意我的看法。准备阅读和理解以任何一种方式编写的代码。)
(C ++有不同的规则。给定struct blah
的声明,你可以将类型称为blah
,即使没有typedef。使用typedef可能会使你的C代码多一点C ++ - 像 - 如果你认为这是一件好事。)
答案 2 :(得分:88)
另一个未指出的区别是给结构命名(即struct myStruct)也使您能够提供结构的前向声明。所以在其他文件中,你可以写:
struct myStruct;
void doit(struct myStruct *ptr);
无需访问定义。我建议您将两个例子结合起来:
typedef struct myStruct{
int one;
int two;
} myStruct;
这为您提供了更简洁的typedef名称的便利,但如果需要,仍允许您使用完整的结构名称。
答案 3 :(得分:53)
在C(非C ++)中,您必须声明结构变量,如:
struct myStruct myVariable;
为了能够使用myStruct myVariable;
,您可以typedef
结构:
typedef struct myStruct someStruct;
someStruct myVariable;
您可以将struct
定义和typedef
组合在一个声明匿名struct
和typedef
的声明中。
typedef struct { ... } myStruct;
答案 4 :(得分:28)
如果您使用struct
而不使用typedef
,则必须始终写
struct mystruct myvar;
写
是违法的mystruct myvar;
如果您使用typedef
,则不再需要struct
前缀。
答案 5 :(得分:23)
在C中,结构,联合和枚举的类型说明符关键字是必需的,即您始终必须在类型的名称(其标记)前加struct
,{{1} }或union
在引用类型时。
您可以使用enum
删除关键字,这是一种信息隐藏形式,因为在声明对象时,实际的对象类型将不再可见。
因此建议(仅参见Linux kernel coding style guide,第5章)仅在此时执行此操作 你实际上想隐藏这些信息,而不仅仅是保存一些按键。
何时使用typedef
的示例将是一个opaque类型,它只与相应的访问器函数/宏一起使用。
答案 6 :(得分:7)
您无法使用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 :(得分:6)
以下代码使用别名myStruct
创建一个匿名结构:
typedef struct{
int one;
int two;
} myStruct;
如果没有别名,则无法引用它,因为您没有为结构指定标识符。
答案 8 :(得分:5)
使用struct
时会出现差异。
您需要做的第一种方式:
struct myStruct aName;
第二种方式允许您删除关键字struct
。
myStruct aName;
答案 9 :(得分:4)
与其他构造一样,typedef
用于为数据类型指定新名称。在这种情况下,它主要是为了使代码更清晰:
struct myStruct blah;
VS
myStruct blah;
答案 10 :(得分:3)
我看到有一些澄清是为了这个。 C和C ++不以不同方式定义类型。 C ++最初只不过是在C之上的另一组包含。
几乎所有C / C ++开发人员现在面临的问题是:a)大学不再教授基础知识,b)人们不理解定义和声明之间的区别。
这种声明和定义存在的唯一原因是链接器可以计算结构中字段的地址偏移量。这就是大多数人逃避实际写错的代码的原因 - 因为编译器能够确定寻址。当有人试图提前做某事时,例如队列,链表,或者支持O / S结构,就会出现问题。
声明以'struct'开头,定义以'typedef'开头。
此外,struct具有前向声明标签和已定义的标签。大多数人都不知道这一点,并使用前向声明标签作为定义标签。
错:
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;
现在,编译器知道虽然它不知道整个类型是什么,但它仍然可以使用前向引用来引用它。
请正确声明并输入您的结构。实际上有一个原因。
答案 11 :(得分:2)
使用后一个示例,在使用结构时省略struct关键字。所以代码中的每个地方都可以写:
myStruct a;
而不是
struct myStruct a;
这节省了一些打字,可能更具可读性,但这是一个品味问题