原本以为结构体的大小就是里边各个成员变量的大小总和,但是运行结构并不是这样,原来结构体大小计算还有一个规则——内存对齐
1.第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
从零偏移处开始,按字节大小计算,判断此偏移地址是否为该成员变量和对齐参数两者之间的最小值,若是,则从此处开始占用内存,大小为该类型所占字节数值,若不是,则内存向后偏移到最小值整数倍处,再开始占用空间。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所最大对齐数(含嵌套结构体的对齐数)的整数倍。
举个栗子:
struct S1
{
char c1;
int i;
char c2;
};
这里就不加运行结果的图片了,结果是 12 。因为我用的是 VS,所以默认对齐数为 4,第一个 c1 的大小是1,所以对齐数为 1,第二个 i 的大小为 4,所以对齐数是 4,与前边的 c1 对齐,则 c1 要再向后偏移 3 个字节,成为 4 个字节才能对齐,最后一个 c2 的大小是 1 ,对齐数也就是 1 ,加在一起就是 4+4+1 = 9,因为还有一个规则是结构体总大小要是最大对齐数的整数倍,现在的最大对齐数是 4 ,要是 4 的整数倍,则需要再补上 3 个字节总大小就是 12 个字节,也就是最大对齐数(4)的整数倍了。
再举个栗子
struct S2
{
char c1;
char c2;
int i;
};
这个结果是 8,因为 c1 和 c2 的对齐数为1,对齐玩=完之后偏移量为 2 ,2 不是 4 的整数倍,所以再往后偏移两位,偏移量为 4 的时候,可以与 i 对齐,与 i 对齐之后,偏移量为 8,也是最大对齐数(4)的整数倍,所以最后结果为 8。
看一个结构体对齐的
struct S1
{
double d;
char c;
int i;
};
struct S2 {
char c1;
struct S1 s1;
double d;
};
int main() {
printf("%d\n", sizeof(struct S1));
printf("%d\n", sizeof(struct S2));
return 0;
}

首先,S1 的大小是 16,d 的大小是 8 ,d 对齐后偏移量为 8,是 1 的倍数,所以 c 对齐后是偏移量是 9 ,9 不是 4 的倍数,偏移到 12是 4 的倍数,再加上 i 结果就是 16 个字节。
S2 的大小,首先 c1 大小是 1,偏移量也即是 1 ,然后对齐结构体 S1,结构体要对齐它的最大对齐数的整数倍(即结构体的大小16),对齐之后偏移量为 18,不是后边 d 的整数倍,直到偏移量为 24 ,d 可以对齐,对齐之后结果为 32 。
当然,默认对齐数是可以改变的。#pragma pack(n) 这个指令就可以改变默认对齐数,n 是要改变的值。
为什么结构体要对齐?
这是一种以空间换时间的做法,数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器
需要作两次内存访问;而对齐的内存访问仅需要一次访问
修改默认对齐数
使用 #pragma pack(n)
n 是多少 就是设置对齐数为多少