结构体(struct)是各种变量组合形成的新数据类型,struct有多种定义形式,总结下来可分四种,初学者容易混淆。下面一并整理区分,其中要特别留意区分三个元素:结构标签名,结构变量名,结构别名。
形式1:
struct 结构标签名
{
类型成员变量名;
......
}结构变量名;
实例:
struct tagPERSON
{
char name[8];
int age;
} person;
这个例子同时定义了一个结构标签名tagPERSON和结构变量person。如果不想放在一起,可选择另一种形式:先定义结构标签,再用结构标签去定义结构变量,如:
形式2:
struct 结构标签名
{
类型成员变量名;
......
}; //先定义结构标签
sruct 结构标签名结构变量名集合; //再用struct+结构标签来定义结构体变量
实例:
struct tagPERSON
{
char name[8];
int age;
};
struct tagPERSON person;
这种方式好处是可以用结构标签一次定义多个同类型结构变量,如:struct tagPERSON Tianyr,Liuqi,Wangming; 但定义时,tagPERSON前面挂个struct多不爽啊,和普通变量的定义还是不一样。要想进一步去掉struct,就要用到typedef,如:
形式3:
typedef struct 结构标签名
{
类型成员变量名;
...
}结构别名; //先定义结构别名
结构别名结构变量名集合; //再用结构别名直接定义变量集合,无需形式2中的struct前缀
实例:
typedef struct tagPERSON
{
char name[8];
int age;
} person_struct;
person_struc person;
这种方式为结构体标签tagPERSON另外定义了一个结构体别名person_struc,可以用这个别名直接定义一个或多个结构体变量,如:string_struc Tianyr,Liuqi,Wangming; 不再需要struct前缀了,并且既然有结构别名,标签名也就可有可无了,于是进一步简化得到:
typedef struct
{
char name[8];
int age;
}string_struc;
实际使用时,这种形式最常见。
形式4:
最后一种,省略形式1中的结构标签,直接定义结构变量,称为无名结构,例如:
struct
{
char name[8];
int age;
}Liuli,Liuqi;
直接定义结构体变量,没有标签名,也没有别名。对于临时结构,这种方式简洁而不留痕迹,适合在函数内部等小作用域使用。
结构体定义的一个常见问题:
用下面代码定义结构时,编译器报错,为什么?
typedef struct tagNode
{
char *pItem;
pNode pNext;
}*pNode;
实现链表时,很多人都碰到同样问题,莫非C不允许结构中包含指向它自身的指针?No,当然允许,这是实现链表的基础。问题在于,一些编译器不支持typedef结构体别名过早使用:pNext的类型是pNode,但pNode是结构体指针的别名,结构体定义完成前,这个别名还不存在。或者说定义pNext时,编译器不认识pNode。这似乎是“鸡生蛋还是蛋生鸡”的死结,其实有以下解决方法:
1)
typedef struct tagNode
{
char*pItem;
struct tagNode *pNext;
}*pNode;
2)
typedef struct tagNode*pNode;
struct tagNode
{
char *pItem;
pNode pNext;
};
用typedef给一个还未定义的类型起新名,C编译器支持这种做法。
3)规范做法:
struct tagNode
{
char *pItem;
struct tagNode *pNext;
};
typedef struct tagNode *pNode;
三种做法利用同一点:标签名在结构体定义初始就可以识别,而结构体别名有时要等结构体定义完之后才能用。所以成员变量类型最好用结构体标签名来定义,不要用别名。
总结
结构标签名、结构变量名以及typedef 重定义的结构别名,三者容易混淆。初学者使用时常常张冠李戴,编译报错后也不知所措,所以要加以重视和区分。
另外enum和union等自定义类型,和struct类似,可参考struct前3种定义形式及“标签/别名/变量名”的概念区分和理解,不重复。