结构体内存对齐有三大法则:
1、数据成员对⻬规则: 结构体(struct)(或联合(union))里的数据成员,假设第一个数据成员是从0开始计算内存大小,之后的每个数据成员存储的起始位置要从该成员大小或者成员的最大子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始。(例如一个int类型成员,其大小为4字节,则要从4的整数倍地址开始存储。假如a的上一个成员是double类型(8个字节),那么a就是从address=9的地方开始计算,9不是4的倍数,就依次往后偏移,一直到12才是4的倍数,便从address=12的地址开始计算内存大小,最终大小等于15)。
2、结构体作为成员: 如果一个结构体里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char、int、double等元素,那b应该从8的整数倍开始存储。)
3、最终内存大小: 结构体的总大小,也就是sizeof的结果。必须是其内部最大成员的整数倍,不足的要补⻬。
基础数据字节大小
下图是基本数据类型分别在64位和32位的字节大小。
举例解析
struct LGStruct1 {
double a; // 8 [0 1 2 3 4 5 6 7]
char b; // 1 [8]
int c; // 4 (9 10 11 [12 13 14 15] **根据结构体内存对齐第一法则,9不是4的倍数,进行偏移,偏移到12**
char d; // 1 [16]
short e; // 2 (17 [18 19] **17不是2的倍数,偏移到18**
char f; // 1 [20]
}struct1; // 总计21个字节,根据结构体内存对齐第三法则,不是最大成员大小的倍数,补齐,最终大小为24个字节
// LGStruct1 sizeof = 24
struct LGStruct2 {
char a; // 1 [0]
double b; // 8 (1 2 3 4 5 6 7 [8 9 10 11 12 13 14 15] **1不是8的倍数,偏移到8**
char c; // 1 [16]
int d; // 4 (17 18 19 [20 21 22 23] **17不是4的倍数,偏移到20**
char e; // 1 [24]
short f; // 2 (25 [26 27] **25不是2的倍数,偏移到26**
}struct2; // 总计27个字节,不是最大成员大小的倍数,补齐,最终大小为32个字节
// LGStruct2 sizeof = 32
struct LGStruct3 {
double a; // 8 [0 1 2 3 4 5 6 7]
int b; // 4 [8 9 10 12]
char c; // 1 [13]
short d; // 2 [14 15]
int e; // 4 [16 17 18 19]
struct LGStruct1 str; // 24 (20 21 22 23 [24 ... 47] **根据结构体内存对齐第二法则,LGStruct1内最大成员的大小是8,20不是8的整数倍,进行偏移,偏移到24**
}struct3; // 总计48个字节,是最大成员大小的倍数(如果该结构体成员里没有double类型,则会按照str成员里的最大成员大小来计算倍数),最终大小为48个字节
// LGStruct3 sizeof = 48
总结
- 计算每个成员的地址,除了第一个以外,其他的首位一定要是该
成员大小的整数倍。 - 如果成员是结构体或数组,首位则是
其中的最大成员的整数倍。 - 最后的内存大小结果要补齐到结构体内
最大成员的整数倍。 - 结构体内同样数量同样类型的成员,如果顺序不同,最终的所占用的内存大小也不一样。