前言
上次我们讲C语言的数据类型分为内置类型和自定义类型,自定义类型包括:结构体,联合,枚举,布尔类型(C99引入),那么今天我们就来详细讲解一下结构体。
1.结构体介绍
1.1为什么要有自定义类型
有同学要问,既然C语言已经提供了内置类型,如:char,int,long,short......那么为什么要有自定义类型呢?假如我们要描述一个人,那么我们就要写出他的身高,体重,性别,年龄等等数据。描述⼀本书需要作者、出版社、定价等。为了解决这个问题,C语言便有了自定义类型,让程序员可以⾃⼰创造适合的类型。
1.2结构体的声明
这就是结构体的声明。其中struct是关键字,用来声明结构体,而tag是结构体的标签,用来标识这个结构体类型,可以在后续代码中使用该标签来声明该结构体类型的变量。member-list是结构体成员变量,variable-list
表示可以直接声明一个或多个结构体变量。切记,大括号后面的分号不能丢。
我们通过下面一段代码来理解结构体:
这段代码我们定义了一个结构体person(此时person相当于tag),而这个结构体包含:high,weight,name,age这四个结构体变量,然后我们使用结构体初始化的方式分别初始化了两个person类型的结构体变量p1,p2;并通过printf打印出了这些数据。
通过这段代码我们明白了结构体如何进行初始化。(关于如何打印这些数据我们下文会讲,请先忽略)这时候细心的小伙伴要问了这里p1和p2创建的地方不同,一个是在main函数内部,另一个在结构体末端。
这就是他俩的区别,一个是全局变量一个是局部变量,它俩的生命周期不同。(不知道这个概念的话可以百度一下,也可以在下面留言,如果人数多的话,下篇文章讲解这个知识点)
1.3结构体成员访问操作符
结构体成员访问的方法有两种,一个是通过“.”操作符,一个是通过“->”操作符。
而“.”操作符是直接访问,“->”操作符是通过结构体指针间接访问。上面我们访问的方式就是直接访问。
使⽤⽅式:结构体变量.成员名(上面的p1,p2就是结构体变量,而high,weight就是成员名)
那么“->”操作符如何使用呢?我们来看下面这段代码
这段代码,我们创建了一个结构体指针变量struct person*pa,指向p1,在通过"->"操作符访问结构体成员变量,从而打印出这些数据。
使⽤⽅式:结构体指针->成员名
接下来我们来看一段综合代码,可以帮助我们更好的理解结构体的定义,结构体成员变量的创建和修改:
为什么我们要用strcpy修改结构体成员变量name呢?
这是因为在C语言中,字符串是字符数组,数组名本质上是数组的首地址,是一个常量指针,是不可修改的。
2.结构体的特殊说明
这个结构体的声明有什么不一样的地方吗?细心的小伙伴肯定能发现它少了tag(标签),那么问题来了
警告:
编译器会把上⾯的两个声明当成完全不同的两个类型,所以是⾮法的。
匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使⽤⼀次。
这个做一下了解即可,现实写代码尽量不要这么写,不然写着写着就会被公司给开除了哈哈。
2.结构体内存对齐
现在我们已经基本了解结构体了,那么新的问题也就来了:结构体的大小是多少?像int类型占4个字节,char类型占1个字节,那么结构体类型占多少个字节呢?
计算之前,我们先要了解一个规则:结构体内存对齐。
2.1结构体内存对齐
对齐规则:
1.结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处
2.其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
对⻬数=编译器默认的⼀个对⻬数与该成员变量⼤⼩的较⼩值。
-- VS 中默认的值为 8
--Linux中gcc没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩
3.结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的
整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构
体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。
结构体成员变量的对其数就是自身的大小,比如int就是4,char对齐数就是1。
笔者也很讨厌这种繁杂的正规解释,我们通过几组练习来了解这个规则:
通过对齐规则,我们可以画出此图,所以S1大小为9吗?(0到8为9个字节)根据第三条规则,这个结构体成员变量的最大对其数为4,故结构体的总大小应该是4的倍数,所以这个结构体所占内存是12个字节,示意图如下:
我们再来一题:
这是解析,答案是8个字节。
3.结构体传参
两个函数,print1和print2,一个传结构体,一个传结构体的地址,哪个更好一点?
答案是:⾸选print2函数。
原因:
函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
如果传递⼀个结构体对象的时候,结构体过⼤,参数压栈的的系统开销⽐较⼤,所以会导致性能的下降。(这个涉及到函数栈帧的知识,想了解的可以百度学习,对自己的“内功”会有所帮助)
结论:
结构体传参的时候,要传结构体的地址。
如果文章对您有帮助,还望一键三连,谢谢支持!!!