1 什么是偏移量
1.1 偏移量是结构体成员变量相对于结构体变量起始地址的相对偏移量。
1.2 第一个结构体成员变量的偏移量为0。
1.3 从第二个结构体成员变量开始,其偏移量为该成员变量前一个成员变量的偏 移量 加上该成员变量的大小。
2 结构体大小的理论计算值
在理论情况下(不进行内存对其的情况下),结构体的大小为最后一个成员变量的偏移量大小加上该成员变量大小。
3 什么是内存对齐
现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是按照固定顺序的一个接一个的排放,这就是对齐。
4 为什么要内存对齐?
4.1 平台原因(移植原因)
不是所有的硬件平台都能访问任意地址上的任意数据,某些硬件平台只能在某些地址处取 某些特定类型的数据,否则抛出硬件异常。
4.2性能原因
经过内存对齐之后,CPU的内存访问速度大大提升。
cpu把内存当成是一块一块的,块的大小可以是2,4,8,16 个字节,因此CPU在读取内存的时候是一块一块进行读取的,块的大小称为(memory granularity)内存读取粒度。
我们再来看看为什么内存不对齐会影响读取速度?
假设CPU要读取一个4字节大小的数据到寄存器中(假设内存读取粒度是4),分两种情况讨论:
1.数据从0字节开始
2.数据从1字节开始
解析:当数据从0字节开始的时候,直接将0-3四个字节完全读取到寄存器,结算完成了。
当数据从1字节开始的时候,问题很复杂,首先先将前4个字节读到寄存器,并再次读取4-7字节的数据进寄存器,接着把0字节,4,6,7字节的数据剔除,最后合并1,2,3,4字节的数据进寄存器,对一个内存未对齐的寄存器进行了这么多额外操作,大大降低了CPU的性能。
5 谁来进行内存对齐
内存对齐由编译器来完成,编译器为程序中的每个数据单元安排在合适的位置上,从而导致了不同的声明顺序的结构体大小不同。
6 内存对齐的规则是什么
- 第一个成员在与结构体变量偏移量为0的地址处。
- 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数=编译器默认的一个对齐数与该成员大小的较小值,在VS环境下默认值为8,在Linux环境下默认值为4。
- 结构体的总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
- 如果嵌套了结构体的情况,被嵌套的结构体对齐到其自身对齐数的整数倍处(结构体的对齐数就是其内部成员中最大的对齐数),此时结构体的整体大小就是所有最大对齐数(含被嵌套结构体的对齐数)的整数倍。
7 看一个例子
struct student{
char a;
int b;
char c;
};
第一个变量 a 的偏移量为1 占一个比特位--------对应规则1
第二个变量 b 要对齐到 它的对齐数(4)的位置,所以b的对齐首地址为4,占4个比特位-----对应规则2
第三个变量 c的要对齐到它的对齐数(1)整数倍的位置,所以c的对齐首地址为9,占一个比特位 -----对应规则3
此时结构体的大小为 1(char)+偏移量3 +int 4 + char 1 =9;
此时需要满足规则4 即结构体本身的对齐, 结构体大小应该是最大对齐数的整数倍 ,变量 a b c 的 对齐数依次 为1 4 1 ,最大对齐数为4 所以结构体大小为4 的倍数 为保证不浪费内存 所以结构体大小为 12 (4 的整数倍)
8 再看一个例子
struct student{
char a;// 对齐首地址为 0 占一个比特位
char b;// 对齐首地址为 b的对齐数的整数倍 b的对齐数为 1 此时对齐的首地址为1 占一个比特位
int c; // 对齐首地址为 c 的对齐数的整数倍 c的对齐数为4 此时对齐的首地址应该为4 占4 个比特位
};
结构体大小为 1(char)+1(char) + 2个偏移量 + 4(int) = 8
9最后一个例子(结构体嵌套)
struct S1
{
char c1;
char c2;
int i;
};
struct S2
{
char c1; // 0~7
struct S1 ; //8~16 占8个比特位
double d; // 16~24 // 占8个比特位
};
结构体S2的大小为 1 (char) +7个偏移量 +8(s1) + d(double)=24
参考资料 https://blog.csdn.net/shanghx_123/article/details/79679726
参考资料 https://www.cnblogs.com/jijiji/p/4854581.html