今天上了数据结构课程的第一堂课,经常会看到下面这种语法:结构体有个成员变量是指向该结构体的指针,也就是自引用(self reference)。翻看了下一章节内容,才知道这是链表的结构基础。平时C语言用的比较少,借此机会重新复习了下结构体,在此记录相关知识。
结构体的声明
我们在定义结构体时,常常会用到两种方式。
方式1. 使用结构标签
用标签变量赋予结构体一个名字,这样定义结构体之后,可以用标签来声明结构体变量。
/*
**标签(tag)为成员列表提供一个名字,可以在后续的声明中使用
**下面这个声明,将标签PEOPLE与成员列表联系在一起(也就是定义了一个名为PEOPLE的结构体),
**并声明了名为Man与Women的两个结构体变量
*/
struct PEOPLE{
int a;
char b;
float c;
}Man,Women;//这里是声明的同时创建了两个变量。
/*
** 变量的声明
** 声明变量时,需要在标签名前加上关键字struct
*/
struct PEOPLE Boy,Girl;//声明了结构体变量 Boy 与 Girl
注意:声明结构体变量时一定要在前面加上struct关键字。但这样难免有些繁琐,所以大家都喜欢用下面介绍的方式2
方式2. 利用typedef技巧
/*
**typedef用于定义一个新的类型名,可以认为是为结构体起一个别名
**此段代码将原来的 struct PEOPLE 重新定义为类型people
*/
typedef struct PEOPLE{
int a;
char b;
float c;
}people;
/*
**变量的声明
*/
people Women,Girl; //声明了结构体变量 Women 与 Girl;
people Boy[20]; //声明了元素类型为结构体people的数组 Boy
可以看到,用typedef定义结构体后,结构体变量的声明变得十分方便。
结构体的自引用
结构体的自引用(self reference),就是在结构体内部,包含指向自身类型结构体的指针。这是实现链表结构的基础。
我们知道,顺序表的数据元素依次存放于一组地址连续的存储单元。而链表是用一组任意的存储单元来存放元素的,这些存储单元可以零散分布在内存的任何位置,大大提高存储器的使用效率。那么问题来了,既然存储单元地址不是连续的,那么前一个节点如何知道下一个节点的地址呢?这里就用到了结构体的自引用。在前一个节点(一个结构体变量)的成员变量中,把那个指针定义为下一个节点的地址,这样层层连接,实现了链表的存储。
但是,为什么定义结构体时,该成员变量是一个指针,而不直接声明该结构体自己呢?
https://blog.csdn.net/gzbaishabi/article/details/34926699
请看这篇文章,表述的非常好。
下面仍旧以两种结构体声明方式,分开讨论
方式1. 使用结构标签
错误的方式
/*
**下面这种声明是错误的。
**PEOPLE是一个结构体,成员变量b的仍旧是结构体
**在分配内存时,由于无限嵌套,编译器无法确定PEOPLE的长度
*/
struct PEOPLE{
int a;
struct PEOPLE b;
};//非法
正确的方式
/*
**由于指针的长度是确定的(指针长度为4),所以编译器能够确定该结构体的长度
*/
struct PEOPLE{
int a;
struct PEOPLE *b;
};
方式2. 使用typedef
错误的方式
/*
**下面这种声明是错误的。
**使用typedef时,程序执行到该段结束才完成了类型PEOPLE的定义
**因此,虽然这里用了指针,但还是错误,因为还没完成类型的定义
*/
typedef struct{
int a;
struct people *b;
}people;//非法
正确的方式
/*
**利用结构标签进行指针的声明
*/
typedef struct tag{
int a;
struct tag *b;
}people;