内存对齐不是什么花里胡哨的理论玩意儿,它是硬件决定的铁律。忽略它,你的代码要么崩溃,要么性能烂到家。现代C++给了你工具,让你精确控制对齐,而不是祈祷编译器聪明。

为什么对齐重要?

  1. 硬件要求:CPU从内存读取数据必须按特定边界(如4字节、8字节、16字节)。不对齐,访问非法,段错误伺候。
  2. 性能杀手:不对齐导致多次内存访问、缓存失效。SIMD指令(SSE/AVX)强制16/32字节对齐,否则零矢量惩罚。
  3. 缓存线:现代CPU缓存64字节。不对齐跨缓存线,延迟翻倍。

记住:对齐是优化,不是可选。过度对齐浪费空间,但不足齐更糟。

alignof:查询对齐需求

alignof(T) 返回类型T的最小对齐字节数。简单、直观。

#include <print>

int main() {
    std::print("alignof(char): {}\n", alignof(char));
    std::print("alignof(int): {}\n", alignof(int));
    std::print("alignof(double): {}\n", alignof(double));
    std::print("alignof(long long): {}\n", alignof(long long));
    return 0;
}

平台相关,但x86_64通常这样。结构体对齐取最大成员对齐,并padding填充。

alignas:强制指定对齐(C++11)

alignas(N)alignas(T) 指定对齐。N必须是2的幂。

alignas(16) char buffer[64];  // 16字节对齐,SIMD友好

struct alignas(32) SseVector {  // AVX对齐
    float data[8];
};

结构体陷阱:成员按声明顺序对齐,padding自动插入。想零padding?用#pragma pack,但那是C风格垃圾,现代用alignas+手动布局。

struct Bad {
    char a;      // 1字节 + 3字节padding
    int b;       // 4字节
    // sizeof(Bad) == 8
};

struct Good {
    alignas(int) char a;  // 强制int对齐
    int b;
    // sizeof(Good) == 8,无浪费
};

std::align:手动内存对齐(C++11)

动态分配不对齐内存时,用std::align调整指针。

#include <memory>
#include <cstddef>

void* raw = operator new(1024);  // 原始内存
size_t space = 1024;
void* aligned = nullptr;

aligned = std::align(32, 1, raw, space);  // 32字节对齐,分配1字节
if (aligned) {
    // 用aligned...
}
operator delete(raw);  // 释放原始

完美用于自定义allocator或对象池。别手动算偏移,那是脑残行为。

对齐的new/delete(C++11+)

C++11引入over-aligned new:

struct alignas(64) CacheLine {  // 缓存线对齐,避免false sharing
    int data;
};

CacheLine* p = new CacheLine;  // 自动64字节对齐
delete p;

编译器生成特殊new/delete处理over-alignment。C++17引入std::align_val_t标签,支持显式over-aligned分配:

#include <new>  // std::align_val_t

struct alignas(64) CacheAligned {
    int data[16];
};

auto p = new(std::align_val_t(64)) CacheAligned();
delete p;

此语法允许自定义allocator精确控制对齐。注意:delete无需标签,编译器自动匹配。

现代特性与最佳实践(C++17/20/23)

  • std::hardware_destructive_interference_size:硬件破坏性干扰大小(C++17,<new>),通常64字节(缓存线大小)。多线程神器:用alignas(此值)对齐独立变量,避免false sharing——同一缓存线修改一个,全线失效,性能暴跌。
  • std::hardware_constructive_interference_size:硬件构造性干扰大小,通常同上。同一缓存线内相关访问可共享预取,优化性能。
alignas(std::hardware_destructive_interference_size) struct Padding {};  // 防false sharing
  • SIMDalignas(64)数组用于AVX512。
  • Allocator:自定义std::allocator支持align_val_t
  • constexpr alignof/alignas:C++17起constexpr友好。

陷阱

  1. alignas(3)非法,必须2幂。
  2. 全局/静态变量对齐影响整个段。
  3. 数组第一个元素决定对齐。
  4. 过度对齐:alignas(4096)浪费页。

忠告

  • 先用alignof,别猜。
  • 小函数测试对齐,别写100行调试。
  • 性能敏感?基准测试,别幻想。
  • 兼容性:MSVC/GCC/Clang行为一致,但ARM不同。

对齐简单,做对就行。别让padding毁了你的数据布局。