学习目标:
在上一章中,我们浅浅地学习了初阶指针的相关知识,而这一章中,我们要学习结构体。可能有很多小伙伴不知道或者没有听过这个名词,在前面C语言总结中的数据类型中,我们讲述了一些数据类型,但是在生活中,肯定有这些结构体所不能表示的复杂数据类型,这样在C语言中,我们为了可以表示这些复杂数据类型,我们引出了结构体的概念!
学习内容:
通过上面的学习目标,我们可以列出要学习的内容:
- 结构体类型的声明
- 结构体初始化
- 结构体成员的访问
- 结构体传参
一、结构体类型的声明
1.1 结构的基础知识
小编先在这里声明一下:标题中的结构就是结构体的意思。结构是一些值的集合,这些值称为成员变量。结构的每一个成员可以是不同类型的变量。这里是不是很熟悉,在之前数组的章节中,数组是存放一组相同类型的元素的集合。
这一小节主要了解一下结构体的概念以及将其与数组区分开了,重点还是数组与结构体的区别!
1.2 结构的声明
struct tag{ member list; //成员变量的列表 }variable-list; //变量列表
为什么要学习结构体呢?
因为当前学习过的类型都是内置类型,比如:char、short、int、double……而如果我们想用C语言描述一个人的时候,怎么描述:人有名字、性别、年龄、电话、地址等因素,因此称其为复杂对象,人的一些因素就对应结构体中的成员列表,这就引入了结构体。
下面我们来举个例子:
struct Stu { //学生的相关属性 char name[20]; int age; char sex[5]; char add[30]; }s3, s4; //s3,s4是结构体类型的变量,是全局变量 int main() { struct Stu s1; //s1是结构体类型的变量,是局部变量 }
注意事项:
在声明结构体变量的时候,可以将 struct 省略吗?一般是不可以的,但是如果你使用typedef 对结构体进行重命名,这样就可以省略 struct:
typedef struct Stu { //学生的相关属性 char name[20]; int age; char sex[5]; char add[30]; }Stu; //Stu是重命名产生的新的类型 Stu s5; //这样就可以了
1.3 结构成员的类型
结构的成员可以是标量、数组、指针,甚至是其他结构体。
1.4 结构体变量的定义和初始化
结构体变量的定义(全局变量和局部变量)有几种方法:
//还是以学生的结构体为例 struct Stu { //学生的相关属性 char name[20]; int age; char sex[5]; char add[30]; }s1; //s1是全局变量 struct Stu s2; //s2是全局变量 int main(){ struct Stu s3; //s3是局部变量 }
结构体变量的初始化操作:
数组的初始化是用大括号,结构体变量的初始化也是使用大括号进行赋值。为了使读者会进行其初始化操作,我们来使用一个比较复杂的结构体类型:
struct B { int c; char ch; }; struct s { char c; int num; int arr[10]; double* pd; struct B sb; struct B* ptr; }; int main() { double b = 3.78; //按照顺序初始化 struct s s1 = { 'a', 3, { 1,2,3 }, &b, {4, 'f'}, NULL }; //局部变量 //指定成员初始化 struct s s2 = { .num = 400, .arr = {5,6,7} }; }
二、结构体成员的访问
1. 结构体变量.成员名
2. 结构体指针->成员名
在前面的操作符中,我们了解了这两个操作符,下面让我们进一步深入研究。在讲之前,我们在来回顾一个易错点:
struct S { int age; char name[20]; }; void my_set(struct S t) { t.age = 19; strcpy(t.name, "zhangsan"); } int main() { struct S s = { 0 }; my_set(s); }
这是一个典型的错误,经典的错误,标准的零分。形参是实参的临时拷贝,改变形参的值不会影响实参的值。
改进方法:
void my_set(struct S* ps) { ps->age = 19; strcpy(ps->name, "zhangsan"); }
三、结构体传参
下面我们来写一下打印结构体的函数:
void my_print1(struct S t) { printf("%d %s", t.age, t.name); } void my_print2(struct S* ps) { printf("%d %s", ps->age, ps->name); }
这两种函数都可以将结构体的内容打印出来,但是这两个函数哪个更优呢?
答案:肯定是第二个。
原因:
函数传参的时候,我们需要将参数进行圧栈的;如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降。
总结:结构体传参的时候,尽量传结构体的地址。
四、总结
这一章的内容还是比较简单,也是比较少的,如果觉得这一章的知识掌握的非常好,可以去看一下我的另外一篇关于结构体的知识:结构体进阶 。
学习产出:
- 结构体类型的声明
- 结构体初始化
- 结构体成员的访问
- 结构体传参