1. 结构的声明
1.1 基础知识
数组表示的是一组相同类型元素的集合,结构体表示的也是一组集合,只不过这组集合中元素的类型不必是相同的。就像数组一样,结构体也是一种数据类型。要想使用结构体的话,就要先创建这种类型(结构体的声明),然后再将它实例化(结构体变量的定义和初始化)。
1.2 结构体的声明
创建学生类结构体。(结构体成员的类型可以使标量、数组、指针或者其他结构体)
struct stu
{
char name[20]; //学生姓名
int age; //学生年龄
int id; //学生学号
};
1.3 结构体的创建与初始化
struct stu
{
char name[20];
int age;
int id;
}s1 = {"Mike",19,20221216}; //①可以在结构体声明之后创建结构体变量,并进行初始化,此处为全局变量
int main()
{
struct stu s; //②可以在main函数中创建结构体变量,此处为局部变量
s = { "jack",18,20221217 }; //初始化
struct stu s2 = { "jack",18,20221217 };
}
2. 结构体成员的访问
得到的是指向结构体的指针,则需使用“->”对其访问。直接得到结构体变量则通过“.”就可以进行访问。
struct M
{
int a;
int b;
};
struct stu
{
char name[20];
struct M m;
};
int main()
{
struct stu s = { "Jack",{5,6}};
printf("%s %d %d ", s.name, s.m.a, s.m.a); //结构体变量访问成员
struct stu *p = &s;
printf("%s %d %d ", p->name, p->m.a, p->m.a); //指向结构体变量的指针访问成员
printf("%s %d %d ", (*p).name, (*p).m.a, (*p).m.a); //指向结构体变量的指针访问成员
}
3. 结构体传参
结构体参数传递分为两大类,第一类是直接将结构体传递过去(传值调用),第二类是将结构体的地址传递过去(传址调用)。
struct M
{
int a;
int b;
};
void print1(struct M m)
{
printf("%d %d", m.a, m.b);
}
void print2(struct M *p)
{
printf("%d %d", p->a, p->b);
}
int main()
{
struct M m1 = { 3,6 };
print1(m1); //传值调用
print2(&m1); //传址调用
}
一般情况下,我们会优先选择传址调用而不是传值调用。原因:函数调用的时候,都会进行压栈操作。如果结构体过大,则参数压栈的系统开销也比较大,会导致系统开销变大(既浪费空间,又浪费时间)。倘若只是传递一个四字节或者八字节的地址,则会系统开销一定会比传递结构体小(节省空间,节省时间)。因此,优先选择传址调用。
附:调用函数时栈区内的变化
栈是一种数据结构,特点是先进后出。一个C/C++编译的程序占用的内存区域主要有栈区、堆区、静态区、文字常量区和程序代码区。栈区由编译器自动分配释放,存放函数的参数值和局部变量的值等,其操作方式类似于数据结构中的栈(此处文字来源于另一篇博主的博客传送门)。压栈指的是存放数据,出栈则是删除数据。
编译程序时,系统首先为main函数在栈区内分配一块空间。当在main函数中传值调用函数时,系统再在栈区内开辟一块空间用来存放参数的值(即压栈,倘若结构体过大,则压栈操作中占用得内存就会大,出栈时也就会占用更多的时间,这就是我们一般会选择传址调用而不是传值调用的原因)。然后再另外开辟一块空间供调用的函数使用。当函数调用完毕之后,系统会自动释放掉刚才才开辟的参数区和调用函数区。
该笔记是学习了B站up主:鹏哥C语言 的课程所写。课程传送门