什么是结构体?
- 当单独的几个数据类型不足以表达用户需求时,这时候就需要构建比较复杂的结构体变量.
- 结构体(struct)指的是一种数据结构,是C语言中聚合数据类型的一类。结构体可以被声明为变量、指针或数组等,用以实现较复杂的数据结构。
结构体同时也是一些元素的集合,这些元素称为结构体的成员(member),且这些成员可以为不同的类型,成员一般用名字访问。 - 结构,是一些值的集合,这些值称为成员变量,结构的每个成员可以是不同的类型.
结构的声明
struct tag{
member-list;
}variable-list;
struct
是自定制的类型,用这个类型来抽象信息.
什么又是抽象?
- 是在具体事物的基础上,提取出一些需要的核心信息,再表达出来
- 抽象相对于具体,少了很多"信息"
这里定义好了结构体变量Student
,但是在实际使用中都是以struct Student xx
的 形式出现,十分麻烦,所以我们通常在定义时加上关键词typedef
:
例如描述一个学生,他有名字,年龄,性别,学号:
typedef struct Student{
char name[20]; //名字
int age; //年龄
char sex[5]; //性别
char id[20]; //学号
}Stu;
//注意 这里最后的分号不能省略
通过typedef
关键词的使用,之后结构体变量的使用就简化为Student xx
最后一行的Stu
,与typedef
相结合就可以理解为重命名,所以之后就可以使用Stu xx
,并且将变量扩展为全局变量.
同样这一全局变量的设定也可以单独语句实现:
typedef struct Student Stu;
-
全局变量的设定会少写很多
struct
,但是会有重名问题的风险.结构的成员可以是标量,数组,指针,甚至是其他结构体.
结构体变量的定义和初始化
定义
struct Point{
int x;
int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
初始化
struct Point p3 = { x , y };
struct Stu{ //类型声明
char name[15];
int age;
};
struct Stu s = {"zhangsan" , 20}; //初始化
struct Node{
int data;
struct Point p;
struct Node* next;
}n1 = {10,{4,5},NULL}; //结构体嵌套初始化
结构体成员的访问
.
操作符: 访问结构体成员变量->
操作符: 结构体指针访问指向变量的成员
struct S s;
strcpy(s.name , "zhangsan"); //使用 . 访问name成员
s.age = 20; //使用 . 访问age成员
struct Stu{
char name[20];
int age;
};
void print(struct Stu* ps){
printf("name = %s age = %d\n",(*ps).name,(*ps).age);
printf("name = %s age = %d\n",ps->name,ps->age);
}
int main(){
struct Stu s = {"zhangsan",20};
print(&s); //结构体地址传参
return 0;
}
从二者相同的结果中分析我们可以得到结论:
a -> b
等效于(*a).b
结构体传参
struct S{
int data[1000];
int num;
};
struct S s = {{1,2,3,4},1000};
//结构体传参
void print1(struct S s){
printf("%d\n",s.num);
}
//结构体地址传参
void print2(struct S* ps){
printf("%d\n",ps->num);
}
int main(){
print1(s); //传结构体
print2(&s); //传地址
return 0;
}
我们可以想想上面代码中print1
和print2
那个函数更好?
答案是: print2
函数
原因比较复杂,简单总结为一下一段话:
讲解函数栈帧的时候,我们讲过函数传参的时候,参数是需要压栈的.如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降.
- 结论:
结构体传参的时候,推荐传递结构体地址,这样可以降低开销.
结构体的嵌套
typedef struct School{
Student students[1000]; //之前定义过的结构体变量
int size;
}School;
- 这样的话不能直接嵌套自身这个结构体,如果嵌套了运行结果为
使用了未定义的结构体
. - 而且也无法对这个结构体函数的变量进行
sizeof
操作,因为不知道结构体的大小(内存占存大小)
如果想要嵌套,可以用指针来实现:
typedef struct Student{
char name[1024];
int score;
struct Student* s; //指针 ,嵌套了自身结构体
}student;
- 这样的话,如果执行
sizeof
这个结构体函数定义的变量,就会有明确的占存大小
例如上面这个程序:
sizeof的结果为: 1024 + 4 + 4 = 1032;
嵌套也可能很复杂,多个结构体变量可以互相传参,这就会产生占存动辄上G的结构体,所以谨慎使用嵌套.