typedef结构与结构定义
我是C编程的初学者,但我想知道在定义结构时使用typedef与不使用typedef有何区别。 在我看来,实际上没有什么区别,他们也完成了同样的事情。
struct myStruct{ int one; int two; };
与
typedef struct{ int one; int two; }myStruct;
常见的习惯用法是:
typedef struct X { int x; } X;
它们是不同的定义。 为了使讨论更清楚,我将分开这个句子:
struct S { int x; }; typedef struct SS;
在第一行中,您要在结构体名称空间中定义标识符S
(而不是C ++意义上的)。 您可以使用它并通过将参数的types定义为struct S
来定义新定义types的variables或函数参数:
void f( struct S argument ); // struct is required here
第二行在全局名称空间中添加一个types别名S
,从而允许您只写:
void f( S argument ); // struct keyword no longer needed
请注意,由于两个标识符名称空间不同,因此在结构体和全局空间中定义S
不是错误,因为它不是重新定义相同的标识符,而是在不同的地方创build不同的标识符。
为了使区别更清楚:
typedef struct S { int x; } T; void S() { } // correct //void T() {} // error: symbol T already defined as an alias to 'struct S'
您可以定义一个与struct相同名称的函数,因为这些标识符保存在不同的空格中,但是不能像这些标识符相同的名称那样定义一个与typedef
同名的函数。
在C ++中,它与定位符号的规则有细微的不同。 C ++仍然保留两个不同的标识符空间,但与C中不同的是,当您只在类标识符空间中定义符号时,不需要提供struct / class关键字:
// C++ struct S { int x; }; // S defined as a class void f( S a ); // correct: struct is optional
search规则有哪些变化,而不是定义标识符的位置。 编译器将search全局标识符表,并且在S
未find之后,它将在类标识符内searchS
前面介绍的代码的行为方式相同:
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不能被编译器自动parsing,并且要创build一个对象或者定义一个该types的参数,你必须回退到包含struct
关键字:
// previous code here... int main() { S(); struct S s; }
另外一个不同的地方是给结构一个名字(比如struct myStruct)也可以提供结构的前向声明。 所以在其他文件中,你可以写:
struct myStruct; void doit(struct myStruct *ptr);
而不必访问定义。 我build议你结合你的两个例子:
typedef struct myStruct{ int one; int two; } myStruct;
这给你更简洁的typedef名称的方便,但仍然允许你使用完整的结构名称,如果你需要。
struct
和typedef
是两个完全不同的东西。
struct
关键字用于定义或引用结构types。 例如,这个:
struct foo { int n; };
创build一个名为struct foo
的新types。 名字foo
是一个标签 ; 只有当它紧挨着struct
关键字时才有意义,因为标签和其他标识符在不同的名称空间中 。 (这与namespace
的C ++概念类似,但是比它更受限制。)
一个typedef
,尽pipe名字,没有定义一个新的types; 它只是为现有types创build一个新名称。 例如,给出:
typedef int my_int;
my_int
是int
的新名称; my_int
和int
是完全相同的types。 同样,给定上面的struct
定义,你可以写:
typedef struct foo foo;
该types已经有一个名称, struct foo
。 typedef
声明给出了一个新的名字foo
。
该语法允许您将一个struct
和typedef
到一个声明中:
typedef struct bar { int n; } bar;
这是一个常见的成语。 现在你可以把这个结构体的types作为struct bar
或者像bar
一样引用。
请注意,typedef名称在声明结束之前不可见。 如果结构包含一个指向它自己的指针,则可以使用struct
版本来引用它:
typedef struct node { int data; struct node *next; /* can't use just "node *next" here */ } node;
有些程序员会为结构标签和typedef名称使用不同的标识符。 在我看来,这没有什么好的理由。 使用相同的名字是完全合法的,并且使得它们更清楚,它们是相同的types。 如果你必须使用不同的标识符,至less要使用一致的约定:
typedef struct node_s { /* ... */ } node;
(就个人而言,我更喜欢省略typedef
并将这个types称为struct bar
, typedef
保存了一些input,但是隐藏了它是一个结构types的事实,如果你想让这个types是不透明的,如果客户端代码是按名称引用成员n
,那么它不是不透明的;它显然是一个结构,在我看来,把它作为一个结构是合理的,但是很多聪明的程序员不同意我在这一点上,准备阅读和理解任何一种写法。)
(C ++有不同的规则,给定一个struct blah
的声明,你可以引用types为blah
,即使没有typedef。使用typedef可能会让你的C代码更像C ++ – 如果你认为这是一个好的事情。)
在C(而不是C ++)中,你必须声明结构variables,如:
struct myStruct myVariable;
为了能够使用myStruct myVariable;
相反,您可以typedef
结构:
typedef struct myStruct someStruct; someStruct myVariable;
您可以将struct
definition和typedef
s合并到一个声明匿名struct
和typedef
的声明中。
typedef struct { ... } myStruct;
在C中,结构,联合和枚举的types说明符关键字是强制性的,也就是说,当引用types时,您总是必须在types的名称(它的标记 )前加上struct
, union
或enum
。
您可以使用typedef
来删除关键字,这是一种隐藏信息的forms,因为在声明对象时,实际types将不再可见。
因此,build议(例如参见Linux内核编码风格指南 ,第5章)只在实际上想要隐藏这些信息时这样做,而不仅仅是为了节省一些击键。
你应该使用typedef
一个例子是一个不透明的types,它只能用于相应的存取函数/macros。
如果你使用没有typedef
struct
,你将永远不得不写
struct mystruct myvar;
写作是非法的
mystruct myvar;
如果使用typedef
,则不再需要struct
前缀。
当你使用这个struct
时候,会有不同。
你必须做的第一个方法是:
struct myStruct aName;
第二种方法允许您删除关键字struct
。
myStruct aName;
typedef
和其他的结构一样,用来给数据types一个新的名字。 在这种情况下,主要是为了使代码更清洁:
struct myStruct blah;
与
myStruct blah;
您不能使用typedef struct
前向声明。
struct
本身是一个匿名types,所以你没有一个实际的名字来转发declare。
typedef struct{ int one; int two; } myStruct;
像这样的前向声明将不起作用:
struct myStruct; //forward declaration fails void blah(myStruct* pStruct); //error C2371: 'myStruct' : redefinition; different basic types
下面的代码创build一个具有别名myStruct
的匿名结构:
typedef struct{ int one; int two; } myStruct;
你不能没有别名来引用它,因为你没有指定结构的标识符。
我看到一些澄清是为了这个。 C和C ++不会以不同的方式定义types。 C ++本来只不过是在C之上的一个附加集合。
几乎所有的C / C ++开发人员今天所面临的问题是:a)大学不再教授基础知识; b)人们不了解定义和声明之间的区别。
这种声明和定义存在的唯一原因是,链接器可以计算结构中字段的地址偏移量。 这就是为什么大多数人摆脱了实际写入不正确的代码 – 因为编译器能够确定寻址。 当有人试图进行某些事情时,问题就出现了,比如队列,链表,或者支持O / S结构。
声明以“struct”开始,定义以“typedef”开头。
此外,结构体具有前向声明标签和定义的标签。 大多数人不知道这一点,并使用正向声明标签作为定义标签。
错误:
struct myStruct { int field_1; ... };
他们只是使用forward声明来标注结构,所以现在编译器已经意识到了这一点 – 但它不是一个实际定义的types。 编译器可以计算出寻址 – 但是这不是它打算如何使用的原因,因为我将暂时显示。
使用这种forms的声明的人,必须始终把“结构”实际上每提及一次,因为它不是一个官方的新types。
相反,任何不引用自身的结构都应该这样声明和定义:
typedef struct { field_1; ... }myStruct;
现在它是一个实际的types,使用时可以在'myStruct'中使用,而不必用'struct'这个单词加上它。
如果你想要一个指向该结构的指针variables,那么包含一个辅助标签:
typedef struct { field_1; ... }myStruct,*myStructP;
现在你有一个指向该结构的指针variables,自定义它。
前瞻性宣言 –
现在,这是一些花哨的东西,前进声明如何工作。 如果你想创build一个引用自己的types,比如链接列表或者队列元素,你必须使用前向声明。 编译器不考虑定义的结构,直到它到达最后的分号,所以它只是在该点之前声明。
typedef struct myStructElement { myStructElement* nextSE; field_1; ... }myStruct;
现在,编译器知道虽然它不知道整个types是什么,但仍然可以使用前向引用来引用它。
请正确声明并键入你的结构。 其实有一个原因。
在后面的例子中,当使用结构时,你省略了struct关键字。 所以在你的代码中,你可以写:
myStruct a;
代替
struct myStruct a;
这样可以节省一些打字的时间,而且可以更具可读性,但这是一个有趣的问题