我是C编程的初学者,但我想知道在定义结构时使用typedef与不使用typedef有什么区别。在我看来,它们真的没有区别,它们实现了相同的目标。
typedef
struct myStruct{int one;int two;};
vs.
typedef struct{int one;int two;}myStruct;
与其他构造一样,typedef用于为数据类型赋予新名称。在这种情况下,它主要是为了使代码更清晰:
struct myStruct blah;
myStruct blah;
在C(不是C++)中,你必须声明如下结构变量:
struct myStruct myVariable;
为了能够使用myStruct myVariable;代替,你可以typedef结构:
myStruct myVariable;
typedef struct myStruct someStruct;someStruct myVariable;
您可以将struct定义和typedef定义组合在一个声明匿名struct和typedef的语句中。
struct
typedef struct { ... } myStruct;
当您使用struct时,差异就出现了。
你要做的第一个方法:
struct myStruct aName;
第二种方法允许您删除关键字struct。
myStruct aName;
如果你使用struct而不使用typedef,你将永远不得不写
struct mystruct myvar;
写作是违法的
mystruct myvar;
如果您使用typedef,则不再需要struct前缀。
在后一个示例中,您在使用结构时省略了struct关键字。因此,在代码的任何地方,您都可以编写:
myStruct a;
而不是
struct myStruct a;
这节省了一些键入,可能更具可读性,但这是一个品味问题
常见的习语是使用两者:
typedef struct S {int x;} S;
它们是不同的定义。为了使讨论更清楚,我将把句子拆分:
struct S {int x;}; typedef struct S S;
在第一行中,您在结构名称空间中定义了标识符S(不是C++意义上的)。您可以使用它并通过将参数的类型定义为struct S来定义新定义类型的变量或函数参数:
S
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函数之后,编译器无法自动解析structS,要创建对象或定义该类型的参数,您必须回退到包含struct关键字:
// previous code here...int main() {S();struct S s;}
在C中,结构、联合和枚举的类型说明符关键字是强制性的,即在引用类型时,您始终必须在类型名称(其标签)前加上struct、union或enum。
union
enum
您可以使用typedef来删除关键字,这是一种信息隐藏形式,因为声明对象时,对象的实际类型将不再可见。
因此,建议(参见Linux内核编码风格指南,第5章)仅在以下情况下执行此操作您实际上想要隐藏此信息,而不仅仅是保存几个击键。
什么时候应该使用typedef的一个例子是一个不透明的类型,它只与相应的访问器函数/宏一起使用。
另一个没有指出的区别是,给结构一个名称(即struct mystruct)也可以让你提供结构的前向声明。所以在其他文件中,你可以写:
struct myStruct;void doit(struct myStruct *ptr);
无需访问定义。我建议您结合您的两个示例:
typedef struct myStruct{int one;int two;} myStruct;
这为您提供了更简洁的typedef名称的便利,但仍允许您在需要时使用完整的结构名称。
以下代码创建一个别名为myStruct的匿名结构:
myStruct
typedef struct{int one;int two;} myStruct;
您不能在没有别名的情况下引用它,因为您没有为结构指定标识符。
您不能将前置声明与typedef struct一起使用。
typedef struct
struct本身是一个匿名类型,因此您没有要转发声明的实际名称。
像这样的前置声明是行不通的:
struct myStruct; //forward declaration fails void blah(myStruct* pStruct); //error C2371: 'myStruct' : redefinition; different basic types
struct和typedef是两个完全不同的概念。
struct关键字用于定义或引用结构类型。例如,这:
struct foo {int n;};
创建一个名为struct foo的新类型。名称foo是一个标签;只有当它前面紧跟struct关键字时才有意义,因为标签和其他标识符在不同的命名空间中。(这类似于namespaces的C++概念,但更受限制。)
struct foo
foo
namespace
typedef,尽管有名称,但并不定义新类型;它只是为现有类型创建一个新名称。例如,给定:
typedef int my_int;
my_int是int的新名称;my_int和int是完全相同的类型。类似地,给定上面的struct定义,您可以编写:
my_int
int
typedef struct foo foo;
该类型已经有一个名称struct foo。typedef声明为同一类型提供了一个新名称foo。
语法允许您将struct和typedef组合成一个声明:
typedef struct bar {int n;} bar;
这是一个常见的习惯用法。现在您可以将此结构类型称为struct bar或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,那么它不是不透明的;它明显是一个结构,在我看来,将其称为结构是有意义的。但很多聪明的程序员在这一点上不同意我的观点。准备好阅读和理解以任何方式编写的代码。)
n
(C++有不同的规则。给定struct blah的声明,即使没有typedef,您也可以将类型引用为blah。使用typedef可能会使您的C代码更C++-如果您认为这是一件好事。)
struct blah
blah
我看到一些澄清是为了这个。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;
现在,编译器知道,虽然它还不知道整个类型是什么,但它仍然可以使用前向引用来引用它。
请正确声明和键入你的结构。实际上是有原因的。