前言
我们知道,通过不同的数据类型我们可以表达不同意义的数据,如长度宽度面积只需要定义一个 int 类型的数据就可以符合大部分的需求。但是,有些东西自存在就是一种集合体,如一个人(他有身高、体重、姓名、爱好等等)一本书(有内容、价格、作者、出版社等等)。如果我们要表达这样的一个存在,以以前的数据结构便无法准确地将其表达。便存在一种拥有多种数据的集合,便叫作结构体。
结构体的定义
这样子便是一个结构体在函数外部的声明,我们可以在函数外定义全局变量,也可以在函数内定义局部变量。不仅如此还有另一种写法叫做匿名结构体类型,就是在声明是时候不加上结构体标签,但是要在结尾加上定义变量的名称。
这样写的有一个不利因素就是,在函数之中要使用的时候定义了几个结构体变量就只能使用几个该结构体。
这时候我们想到,如果这样定义了两个结构体,他们的内容完全一样,那这两个结构体是等价的吗?
运行起来编译器就报错了,即便是内部定义的内容完全一样,但是从编译器的视角来说,这两个结构体并不是同一类型的东西。
结构体的自引用
当我们想要在一个结构体中再包含一个同类型的结构体。
如此,便会看到编译器给我们报了错误,这个语法本身就是错误的,况且当一个结构体类型内部包含了另一个结构体类型,如此一环套一环,这样我们便无法计算这个结构体的具体大小了。
正确定义的方式应该是这样子,next 表示指向结构体的结构体指针,如此便可以正确定义。
结构体的初始化
定义完结构体,如果不进行初始化也无法使用。结构体的初始化也十分地简单,只需要在主函数中定义是在后面用大括号进行初始化。
也可以在定义结构体是直接进行初始化,最终的结果都是一样,只不过前者属于局部变量而后者属于全局变量。
但是我们是可以在一个结构体中嵌套调用另外一个结构体的。
在定义如此结构体的时候也需要嵌套使用两个{ } ,第一个大括号表示我们对所定义的这个结构体也就是 s 进行初始化,第二个大括号则表示对内部嵌套调用的结构体进行初始化。
结构体内存的计算
我们来看这串代码,我们定义了两个结构体变量,内容上的区别只有不同数据类型的排放顺序不同,但是这两个结构体的所占内存的大小好像有所不同。
从结果上看,两个结构体所占的内存并不相同。为什么会出现这种情况呢?我们需要了解结构体内存补齐的机制。
1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。(VS中默认的值为8)
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
之所以存在内存补齐的机制,是为了保证在读取的时候可以完整地读取每个数据,因而对内存进行的修饰。即耗费了内存但是大大地增加了代码运行的速率。
既然已经知道了内存补齐的规律,那我们来分析一下上面的代码。c1由于只有一个字节因此刚开始对齐于与结构体变量偏移量为0的地址处,第二个数据为 int 类型的数据,根据规律其他成员变量要对齐到对齐数的整数倍的地址处,而 int 类型数据的对齐数为4,因此 i 应该对齐到偏移为4的位置。第三个数据类型为 char 对齐数为 1 因而直接对齐于偏移为8的位置,这还没完,结构体总大小为最大对齐数的整数倍。目前位置所占大小为9,最大的对齐数为4,所以要再次进行补齐。即最后的内存大小为12。
同理,第二个结构体,用同样的方法进行推理便可以得到下面的结果。所以总的内存便占了8个字节。
如果喜欢本篇文章就留下个赞吧,关注博主不迷路,也欢迎大家在评论区留言讨论,谢谢大家。