结构体中数据类型对齐--完解
参考:
三步解决C语言中struct字节对齐问题:
https://blog.****.net/m0_37829435/article/details/81348532
结构体对齐规则:
https://www.cnblogs.com/heart-flying/p/9556401.html
结构体字节对齐
先介绍三个概念:自身对齐值、指定对齐值、有效对齐值。
自身对齐值:数据类型本身的对齐值,例如char类型的自身对齐值是1,short类型是2;
指定对齐值:编译器或程序员指定的对齐值,32位单片机的指定对齐值默认是4;
有效对齐值:自身对齐值和指定对齐值中较小的那个。
解决过程
1.先确定指定对齐值,其由以下三个因素决定
(1) CPU周期
WIN vs qt 默认8字节对齐
Linux 32位 默认4字节对齐,64位默认8字节对齐
(2) 结构体最大成员(基本数据类型变量)
(3) 预编译指令#pragma pack(n)手动设置 n--只能填1 2 4 8 16
2.除结构体的第一个成员外,其他所有的成员的地址相对于结构体地址(即它首个成员的地址)的偏移量必须为该类型的有效对齐值 (自身对齐值和指定对齐值中较小的那个)
3. 结构体的整体有效对齐值是其最大数据成员的自身对齐值;
上面三步即是万能公式,下面看实际例子(linux 64系统下):
1.
上面nums中,没有手动设置对齐单位,linux64系统的默认对齐单位是8字节,结构体nums的最大成员double d占8个字节,故实际对齐字节是二者最小,即8字节。
char a放在结构体的起始地址;
short b占2个字节,2小于实际对齐字节8,故b的起始地址相对于a的起始地址的偏移量须为2的整数倍个字节;
int c占4个字节,4小于实际对齐字节8,故c 起始地址相对于a的起始地址的偏移量须为4的整数倍个字节;
double d占8个字节,8与实际对齐字节8相等,故d的起始地址相对于a的起始地址的偏移量须为8的整数倍个字节;
所以nums所占空间如下: 1(a)+1(浪费的空间,由b的起始地址决定这1字节必须腾出)+2(b)+4(c)+8(d)=16个字节
2.在上面结构体最后添加一个char数组,再看情况:
到成员double d为止,结构体nums占的空间是16,上面已经分析过,然后后面是一个char型数组,数组的类型是char[13],并不是基本数据类型,这里仍然当做13个char型变量来处理,char占1个字节,小于实际对齐字节8,所以这13个char型变量可以直接挨着double d后面放(最后结果看起来也就相当于整个数组挨着double d放置);所以总的空间情况是:1(a)+1(浪费空间)+2(b)+4(c)+8(d)+13(arr)=29;但29并不满足上面三步走的最后一步:“整个结构体的大小必须是实际对齐单位的整数倍”,所以29+5(浪费空间)=32,所以最后nums的空间情况是1(a)+1(浪费空间)+2(b)+4(c)+8(d)+13(arr)+5(浪费空间)=32字节
另外结构体嵌套结构体的字节对齐和上面原理一样,唯一要注意的是子结构体的起始地址与母结构体的起地址之间的距离必须是子结构体最大成员或者实际对齐单位(还是取两者小的那个)的整数倍。
输出结果:
网上疑问
结构体自身对齐:是按最长数据类型的有效对齐还是指定对齐方式?
答:最长数据类型的有效对齐
实验:
window64,指定对齐8字节
typedef struct datatype0 {
char a;
char b;
}type0;
typedef struct datatype1 {
char a;
short b;
}type1;
int main() {
printf("type0=%d,type1=%d\n", sizeof(type0), sizeof(type1));
return 0;
}
运行结果:
如果为指定对齐,type0,1都应该是8,显然不是指定对齐方式。