C++ 的内存对齐

§ 参考资料 共 1 条

内存对齐指的是:数据在内存中存储时的起始地址受到限制,同时会影响结构体或类的大小

CPU 访问内存并不是一个字节一个字节地读,而是以 Chunk 为单位,如果不对齐,那么可能需要多读一个 Chunk

对齐值

分为自身对齐值、指定对齐值和有效对齐值三个

自身对齐值通常等于数据类型的大小

  • 对于内置类型,就等于该类型的大小,如:

    • char: 自身对齐值为 1

    • int: 自身对齐值为 4

    • double: 自身对齐值为 8

  • 对于结构体或者类,它的自身对齐值为其成员中最大的那个自身对齐值

指定对齐值是由编译器或者预处理指令强制指定的对齐系数

  • 默认情况下,64 位操作系统默认的对齐数为 8,32 位的为 4
  • 可以使用 #pragma pack(n) 指令,更改指定对齐值为 n

有效对齐值是编译器最终执行的对齐数值

  • 有效对齐值 = 指定对齐值 与 自身对齐值 的较小值
  • 对于结构体或者类,有效对齐值为其成员中最大的那个有效对齐值(和上面是等价的)

对齐

对于结构体内部的各个成员,每个成员的起始位置,完全取决于它自己的有效对齐值

  • 结构体中第一个成员的偏移量为 0
  • 每个成员偏移量必须是该成员的有效对齐值的整数倍

在确定各个成员的位置后,需要确定当前结构体的大小

  • 根据对齐值规则可以求出当前结构体的有效对齐值。最终总大小必须是有效对齐值的整数倍,如果不够会在末尾填充字节
  • 结构体或者类至少占用 1 个字节

C++ 中的相关函数和编译器拓展

C++ 标准

alignof:返回自身对齐值

alignas:增大自身对齐值(不能比默认值小)

// 强制结构体起始地址 32 字节对齐
struct alignas(32) Data {
    float data[8];
};

// 也可以用于变量
alignas(64) int cache_line_isolated_var;

注意几个函数的 align 参数都是有效对齐值

std::align(align, size, buffer_ptr, buffer_size):在 Buffer 中查找计算并返回一个满足特定对齐要求的指针地址

std::aligned_alloc(align, size):在堆上分配对齐的内存

std::assume_aligned<align>(ptr):告诉编译器“我保证这个指针已经是 X 字节对齐的了,请生成更高效的指令

编译器拓展

MSVC:

#pragma pack(n):改变指定对齐值

__declspec(align(n)):增大自身对齐值,与 alignas 一致

_aligned_malloc_aligned_free

GCC/Clang:

__attribute__((packed)):压缩对齐,结构体的自身对齐值设置为 1,并且内部无视对齐规则,紧密排列

__attribute__((aligned(n))):增大自身对齐值,与 alignas 一致

posix_memalign(ptr_of_ptr, align, size)