
必须在需与硬件寄存器、网络协议包、二进制文件或旧ABI严格对齐时使用__attribute__((packed)),它禁用编译器填充字节,使成员紧密排列,但可能导致未对齐访问异常。
__attribute__((packed))
当你需要和硬件寄存器、网络协议包、二进制文件格式或旧有 C 结构体 ABI 严格对齐时,__attribute__((packed)) 才真正必要。比如读写某个 MCU 的外设寄存器结构体,或者解析一段从 socket 直接 recv() 来的 raw packet,编译器默认加的 padding 会导致 offsetof 偏移错乱、memcpy 拷贝越界、甚至触发总线错误。
它禁止编译器为结构体成员插入任何填充字节(padding),让每个成员紧挨着前一个成员存放,哪怕类型本身要求对齐(如 uint32_t 要求 4 字节对齐)。后果是:sizeof 变小,但访问可能变慢,某些平台(ARMv7 以下、RISC-V 默认)还可能触发未对齐访问异常。
struct foo {
uint8_t a;
uint32_t b;
uint8_t c;
};
// sizeof == 12(a:1 + pad:3 + b:4 + c:1 +
pad:3)struct foo {
uint8_t a;
uint32_t b;
uint8_t c;
} __attribute__((packed));
// sizeof == 6(a:1 + b:4 + c:1,无 padding)很多人以为加了 packed 就“安全了”,其实不然:
__attribute__((packed)) 是 GCC/Clang 扩展,MSVC 不识别(要用 #pragma pack(1) 替代)packed
多数场景下,比起依赖编译器扩展,更推荐显式控制布局:
std::memcpy 按字节拷入/出固定 buffer,再用 std::bit_cast(C++20)或 std::memcpy 到目标类型std::array<:byte n>,再用 std::span 和 std::bit_cast 提取字段static_assert(sizeof(foo) == 6); static_assert(offsetof(foo, b) == 1);避免后续修改结构体时 silently 破坏协议
真正难处理的不是 packed 本身,而是跨平台未对齐访问的静默差异——同一段 packed 结构体,在 x86 上跑得好好的,搬到 ARM 上就 crash,这种问题往往要到实机联调才暴露。