结构体
1.结构体类型声明
1.1结构体的概念
结构体是⼀些值的集合,这些值称为成员变量。结构体的每个成员可以是不同类型的变量。
1.2 结构的声明
struct tag
{
member-list;
}variable-list;
例如描述⼀个学⽣:
struct Stu
{
char name[20]; //名字
int age; //年龄
char sex[5]; //性别
char id[20]; //学号
}; //分号不能丢
1.struct是结构体的关键字,不可更改。
2.tag是结构体的名字,或者标签,可以更改。
3.member-list 结构体的成员列表,表示结构体内部的元素。
4.variable-list 结构体的变量成员列表,表示类型是结构体的变量,可以省略。
声明只是表示结构体的类型,并不占内存的空间,只有结构体变量初始化或者进行改变的时候,才会占用内存空间。
1.3特殊声明
在声明结构的时候,可以不完全的声明。
⽐如:
//匿名结构体类型
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20], *p;
上⾯的两个结构在声明的时候省略掉了结构体标签(tag)。
那么问题来了?
//在上⾯代码的基础上,下⾯的代码合法吗?
p = &x;
警告:
编译器会把上⾯的两个声明当成完全不同的两个类型,所以是⾮法的。
匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使⽤⼀次。
1.4 结构的自引用
在结构中包含⼀个类型为该结构本⾝的成员是否可以呢?
拿数据结构中的顺序表举例,自引用就是当你找到一个数字1的时候,你可以通过数字1顺藤摸瓜找到数字2,又可以从数字2顺藤摸瓜找到数字3,以此类推。
而结构体的自引用也是如此,不过其本身应该更类似与数据结构体中的链表。
通过1找到2,通过2找到3,通过3找到4,通过4找到5,这其中的每一个数字都是一个节点,这一个节点包含着前往下一个节点的线索,以此类推便可以顺藤摸瓜找到最后。
换到结构体身上便是一个结构体中包含着下一个结构体,且两个结构体是相同类型的,同名同标签(名 = 标签,不是变量名是结构体名)
那就有了下面的代码
⽐如,定义⼀个链表的节点:
struct Node
{
int data;
struct Node next;
};
上述代码正确吗?如果正确,那 sizeof(struct Node) 是多少?
仔细分析,其实是不⾏的,因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的⼤⼩就会⽆穷的⼤,是不合理的。
正确的自引用方式:
struct Node
{
int data;
struct Node* next;
};
根据链表的特点,我们将节点分为两个部分,一个部分存放的结构体本身的数据,另一个部分存放了下一个同类型同名的结构体的地址,这样便很好的解决了问题。
在结构体⾃引⽤使⽤的过程中,夹杂了typedef对匿名结构体类型重命名,也容易引⼊问题,看看下⾯的代码,可⾏吗?
typedef struct
{
int data;
Node* next;
}Node;
答案是不⾏的,因为Node是对前⾯的匿名结构体类型的重命名产⽣的,但是在匿名结构体内部提前使
⽤Node类型来创建成员变量,这是不⾏的。
解决⽅案如下:定义结构体不要使⽤匿名结构体了
typedef struct Node
{
int data;
struct Node* next;
}Node;
2.2. 结构体变量,全局变量、局部变量:
2.1结构体变量的创建和初始化
有了结构体类型,那如何定义变量,其实很简单,结构体变量的初始化使⽤
结构体初始化
struct Peo
{
char name[20];
char tele[12];
char sex[5];
int high;
};
int main()
{
struct peo p = {
"张三","15596668862", "男", 181};
return 0;
}
包含了其他结构体变量的初始化:
struct Peo
{
char name[20];
char tele[12];
char sex[5];
int high;
};
struct st
{
struct peo p;
int num;
float f;
};
int main()
{
struct st s = {