C++

C++ struct 对齐规则

Posted by KalosAner on March 23, 2025

一、引言

C++ struct 的大小通常不会直接等于各个成员大小的总和,这是因为太过精细地分配内存会导致内存分配和访问的效率过低,所以一般对于不同大小的成员会采取对齐规则来分配内存。

结构体的地址和结构体中第一个成员的地址相同。

二、对齐规则

结构体在对齐时,对于没有用到的空间使用空白字节填充。

规则一:结构体的成员按照定义顺序依次置于内存,每个成员按照自身大小对齐。

结构体中每个成员都有自己的大小,在对齐时成员依次置于内存。设第一个成员的偏移量为 0,则后续每个成员的偏移量都是该成员的倍数。指针类型的成员在 64 为系统下固定占 8 个字节。数组类型的成员可以视为多个基础类型的成员,数量就是数组的大小,基础类型就是数组的类型。

1
2
3
4
5
6
7
8
struct A {
	char c;
    int i;
    double d;
}

printf("%d %d %d %d\n", sizeof(A), &A::c, &A::i, &A::d);
// 16 0 4 8
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
c 填充 填充 填充 i0 i1 i2 i3 d0 d1 d2 d3 d4 d5 d6 d7

规则二:结构体的大小必须是结构体中最大的成员大小的整数倍。

结构体的大小必须是有规则的,这样当两个相同的结构体变量放到内存时,两个结构体也是对齐的。

1
2
3
4
5
6
7
8
struct B {
	char c;
	double d;
    int i;
}

printf("%d %d %d %d\n", sizeof(B), &B::c, &B::d, &B::i);
// 24 0 8 16
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
c 填充 填充 填充 填充 填充 填充 填充 d0 d1 d2 d3 d4 d5 d6 d7 i0 i1 i2 i3 填充 填充 填充 填充

规则三:如果结构体中还有结构体,则内部结构体的大小可以视为内部结构体最大的成员的大小进行对齐。

如果结构体中还有结构体,内部的结构体已经对齐,可以视为一个数组,当作多个普通成员进行对齐。

1
2
3
4
5
6
7
8
9
struct C
{
    char c;
    struct B b;
    int i;
};

printf("%d %d %d %d\n", sizeof(C), &C::c, &C::b, &C::i);
// 40 0 8 32

| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | —- | | c | 填充 | 填充 | 填充 | 填充 | 填充 | 填充 | 填充 | b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7 | b8 | b9 | b10 | b11 | b12 | b13 | b14 | b15 | b16 | b17 | b18 | b19 | b20 | b21 | b22 | b23 | i0 | i1 | i2 | i3 | 填充 | 填充 | 填充 | 填充 |

编译器默认的对齐数是可以通过#pragma pack (n)来指定对齐数,但是修改之后不同的平台可能会不兼容。