内存字节对齐

335 阅读3分钟

尽管内存是以字节为单位,但是大部分处理器并不是按字节块来存取内存的.它一般会以双字节,四字节,8字节,16字节甚至32字节为单位来存取内存,我们将上述这些存取单位称为内存存取粒度.

普通结构体

1.1

typedef struct {
    char a;
    int b;
    char c;
} AStruct;

sizeof(AStruct) = 12

解析:

a => [0], offset = 1
b 长度为 4 个字节,此时 offset 需要 +3,才是4的倍数,offset = 4, b => [4, 7], offset = 8
c 长度为 1 个字节, offset = 8 满足,因此 c => [8]
此时共占用字节 9,由于当前结构体中,最长数据类型 sizeof(int) = 4, 对齐后取值为 12

1.2

typedef struct {
    char a;
    int b;
    char c;
    double d;
} AStruct;

sizeof(AStruct) = 24

解析:

a => [0], offset = 1
b 长度为 4 个字节,此时 offset 需要 +3,才是4的倍数,offset = 4, b => [4, 7], offset = 8
c 长度为 1 个字节, offset = 8 满足,因此 c => [8], offset = 9
d 长度为 8 个字节,offset + 7 = 16 才是 8 的倍数,因此 d => [16,23]
此时共占用字节 24,由于当前结构体中,最长数据类型 sizeof(double) = 8, 24 是 8 的倍数,因此对齐后取值为 24

1.3

typedef struct {
    char a;
    char c;
    int b;
    double d;
} AStruct;

sizeof(AStruct) = 16

解析:

a => [0], offset = 1
c 长度为 1 个字节, offset 满足,因此 c => [1], offset = 2
b 长度为 4 个字节,此时 offset 需要 +2,才是4的倍数,offset = 4, b => [4, 7], offset = 8
d 长度为 8 个字节,offset = 8 是 8 的倍数,因此 d => [8,15]
此时共占用字节 16,由于当前结构体中,最长数据类型 sizeof(double) = 8, 16 是 8 的倍数,因此对齐后取值为 16

结构体嵌套结构体

2.1

typedef struct {
    char a;
    char c;
    int b;
//    double d;
} AStruct;

typedef struct {
    char a;
    AStruct A;
    char c;
} BStruct;

sizeof(AStruct) = 8;
sizeof(BStruct) = 16;

解析:

a => [0], offset = 1
A 长度为 8 个字节,但是 A 中最长数据类型为 sizeof(int) = 4, 此时 offset 需要 +3,才是4的倍数, A => [4, 11], offset = 12
c 长度为 1 个字节, offset = 12 满足,因此 c => [12]
此时共占用字节 13,由于当前结构体中,最长数据类型在 AStruct.b sizeof(int) = 3, 13 不是 4 的倍数,因此对齐后取值为 16

2.2

typedef struct {
    char a;
    char c;
    int b;
    double d;
} AStruct;

typedef struct {
    char a;
    AStruct A;
    char c;
} BStruct;

sizeof(AStruct) = 16;
sizeof(BStruct) = 32;

解析:

a => [0], offset = 1
A 长度为 16 字节,但是 A 中最长数据类型为 sizeof(double) = 8, 此时 offset 需要 +7,才是 8 的倍数, A => [8, 23], offset = 24
c 长度为 1 个字节, offset = 24 满足它的倍数,因此 c => [24]
此时共占用字节 25,由于当前结构体中,最长数据类型在 AStruct.d sizeof(double) = 8, 25 不是 8 的倍数,因此对齐后取值为 32

总结

内存对齐规则

(1) 结构体第一个成员的偏移量(offset)为0,以后每个成员相对于结构体首地址的 offset 都是该成员大小与有效对齐值中较小那个的整数倍,如有需要编译器会在成员之间加上填充字节。
(2) 结构体的总大小为 有效对齐值 的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。

结构体嵌套对齐
结构体作为结构体成员,其对齐值取其自身成员中最长数据类型的成员的长度


参考文章:
C/C++内存对齐详解