一.结构体内存为什么要对齐?
1.移植原因 - 计算机在内存读取数据时,只能在规定的地址处读取数据,而不是内存中任意地址都是可以读取的。不是所有的硬件平台都能访问任意地址上的数据,某些硬件平台只能在某些特定的地址处取某些特定的数据:比如有的只能读取地址处地址为8的倍数或者4的倍数等的数据。
2.效率原因: - 正式因为第一条的原因,在访问一些数据时,对于访问了为对齐的内存,处理器需要2次访问,而对齐的内存只需要一次。为什么不对齐的话要2次呢? 比如结构体中的2个成员char i = 'a'; int j = 100; i占一个字节,int占4个字节, 比如32为的系统读取的时候一次要读4个字节,读取i相当于也读取了j的前3个字节 再去读取j的时候也要先读取i的后3个字节再去读取j。
当结构体内存对齐之后就变成了这样: 这里只是一种情况,其他的要参考结构体所占大小的规则。
再去读取的时候就能一次读取了。
二.结构体所占空间大小
结构体的大小不是所有成员大小简单的相加,需要考虑到系统在存储结构体变量时的地址对齐问题。
注:后面代码都是在windows下vs中执行
计算规则:
1.结构体的第一个成员的地址就是结构体变量偏移量为0的地址处,可以看做是这个结构体内存的头
2.除了第一个的其他成员需要对齐某个数字的整数倍的地址处。这里的某个数字(也可以叫对齐数)为编译器默认的对齐数与该成员的大小比较取最小值。编译器默认的对齐数可以用#pragma pack(show)查询,也可以#pragma pack (N) 修改,N即为修改的值,这个值只能是1,2,4,8,16。注意: 不同的编译器/操作系统对用的默认对齐数可能会有不同,vs中默认是8,Linux默认为4。 成员占的大小为自己所占的大小
3.结构体的总大小要为结构体中最大对齐数的整数倍。
例1:
struct stu1
{
int i;
char c;
double j;
char a;
};
printf_s("sizeof( stu1):%d\n", sizeof(struct stu1)); //结果为24
先介绍一个相关的概念——偏移量。偏移量指的是结构体变量中成员的地址和结构体变量地址 的差。结构体大小等于最后一个成员的偏移量加上最后一个成员的大小。显然,结构体变量中第一个成员的地址就是结构体变量的首地址。因此,第一个成员i的偏 移量为0。第二个成员c的偏移量是第一个成员的偏移量加上第一个成员的大小(0+4),其值为4;第三个成员j的偏移量是第二个成员的偏移量加上第二个成 员的大小(4+1),其值为5,因为有了地址对齐的要求,编译器在编译程序时会遵循上面3个规则:
对照第1条,结构体第一个成员偏 移量为0,所占大小为4字节
对照第2条,上面的例子中前两个成员的偏移量都满足要求,但第三个成员需要对齐8(vs默认对齐数为8,8和4比较取最小值)的整数倍的地址处,但在这里是5的位置,编译器在处理时会在第二个成员后面补上3个空字节,使得第三个成员的偏移量变成8。
对照第3条,结构体总大小要为结构体中最大对齐数的整数倍。也就是8的整数倍,从上面加起来为4+4+8+1 = 17 ,3*8 = 24才能存下这个结构体,所以例子中计算出来的大小为24
例2:
struct A {
char c0; //1
char c1; //1
char c2; //1
char c; //5
double d; //8
int i; //4
int j; //4
char c3; //1 32
};
printf_s("sizeof(A):%d\n", sizeof(struct A)); //结果为32
如果是嵌套结构体,最方便的就是各算各的然后加起来。
注: 以上仅作为本人学习后的总结。