很多面试小伙伴在面试时都会被问到结构体结构体字节对齐这一问题,没有搞懂结构体内存对齐的原理的小伙伴遇到这个问题时就手足无措,一脸懵逼了。下面和大家一起分享学习关于自定义结构体内存对齐的详细内容,一起探索计算机内存的奥秘,话不多说,正题开始。
学习就像剥洋葱,得一层一层的往里剥,才知道有多辣眼睛,
首先开始剥开第一层:
什么事结构体字节对齐?
假如有个学生结构体Student如下,请问它占用了几个字节?
从直观角度来看:char占用1个字节,int占用4个字节,short占用2个字节,因此该结构体总共占用7个字节。
真的是这样吗?让我们交个编译器一探究竟吧,程序测试代码如下:
呀,结构体内部成员分别为1、4、2没错呀,可是结构体为什么是12呢,是不是编译器出错了?我呸,编译器肯定不会错的,接下来进入到结构体内存对齐机制:
结构体内变量的字节对齐需要符合一下三种规则,别问我为什么,问就是强制规定。
- 结构体变量的首地址能够被其最宽基本类型成员大小所整除
- 结构体每个成员相对结构体首地址的偏移量(offset)都是自身有效对齐字节数的整数倍。
- 结构体内变量的自身有效对齐字节数为自身对齐字节数与系统对齐字节数的较小者(即两者中的最小值);结构体的大小为成员最大自身对齐字节数的倍数。
这三条能看懂的同胞可以不必往下看了,不然就是浪费时间,浪费别人时间就是谋财害命。
要弄懂这三句话,我们先理解每个成员的相对于首地址的偏移量(offset)、自身对齐字节数、系统对齐字节数、自身有效对齐字节数、最大自身对齐字节数是什么意思?
每个成员的相对于首地址的偏移量(offset)是指该变量在结构体中的首地址减去结构体中的首地址。
如下例子:
使用gdb进行调试:
设置断点 b 13
分别显示结构体中每个变量的地址,其结果如下图所示:
通过上图显示可知,结构体中变量a的首地址为0x7fffffffdc14和结构体变量首地址一致,其偏移量为0;结构体中变量b的首地址为0x7fffffffdc18,与结构体变量首地址的偏移量为0x7fffffffdc18-0x7fffffffdc14=4;同理同样可得变量c的相对于首地址的偏移量为0x7fffffffdc1c-0x7fffffffdc14=8。
自身对齐字节数:是地结构体内各类型的变量执行sizeof()函数得到的结果,即该变量占用空间的大小(字节数)。
系统有效对齐字节数:是由系统设置的,一般在64位编译器下默认数字是16,可以进行修改。
自身有效对齐字节数:min(自身对齐字节数,系统有效对齐字节数),取较小值。
最大自身有效对齐字节数:max(s所有变量的自身对齐字节数)
继续以结构体Stu为例,计算其所占用字节。
第一:该结构体如下时
结构体中变量a的偏移量为0,自身有效对齐字节数:min(1,16),即1,根据规则第二条,符合偏移量是自身有效对齐字节数的倍数。其最大自身有效对齐字节数也为1,当结构体大小也为1 的时候,符合第三条规则,所以结构体大小为1.
测试结果如下:
第二:在第一的基础上新增一个整形变量b
变量b相对于首地址的偏移量为1,其自身有效对齐字节数为min(4,16)即为4,偏移量1并不是自身有效对齐字节数的倍数,所以不符合规则2;则需要再变量a后面填充3个空字节,使得变量b相对于首地址的偏移量为4,才能满足规则2,填充完之后,其最大自身字节对齐数位4,此时的结构体大小为8,满足结构体大小为最大自身字节对齐数的整数倍规则。
第三:在第二的基础上上添加short变量c
变量c相对于首地址的偏移量为8(a,b已经占了8个字节),其自身有效对齐字节数为min(2,16),即2,符合规则2,最大自身有效对齐字节数为4(int),此时结构体大小为10,然而10贝能被4整除,所以不符合规则三,需要再变量c后继续填充2个字节才能满足条件,填充后,结构体大小为12.
运行结果如下:
关于结构体内存大小以及字节对齐的讲解到此已结束,本人为初学者,若有错漏之处,欢迎各位方家指正。