go结构体大小通常大于字段字节数之和,因编译器按内存对齐规则插入padding;字段顺序影响padding量,应将高对齐字段前置;总大小必为最大字段对齐值的倍数;cgo需显式同步对齐策略。

Go 结构体大小不等于字段字节数之和
直接结论:unsafe.Sizeof 返回的结构体大小,几乎总是大于各字段 unsafe.Sizeof 之和。这不是 bug,是 Go 编译器按内存对齐规则主动插入 padding 的结果。
比如 type ST1 struct { A byte; B int64; C byte },字段加起来才 10 字节,但 unsafe.Sizeof(ST1{}) 在 64 位系统下返回 24 —— 中间被塞了 14 字节 padding。
原因在于:每个字段必须从自身对齐边界开始存放。int64 的 unsafe.Alignof 是 8,所以它前面必须留出足够空间,让它的地址能被 8 整除;而 byte 对齐值是 1,不挑位置,但它的“落脚点”会被前面的大字段强制推后。
字段顺序直接影响 padding 总量
结构体字段声明顺序不是语义无关的装饰,而是决定内存布局的关键变量。编译器不会重排字段,你写成什么样,它就按什么样布局(然后加 padding)。
-
type Bad struct { a bool; b int64; c bool }→ 占 24 字节(a 后填 7 字节对齐 b,c 后再补 7 字节使整体为 8 的倍数) -
type Good struct { b int64; a bool; c bool }→ 占 16 字节(b 占 0–7,a 在 8,c 在 9,末尾补 6 字节到 16)
核心策略:把对齐要求高的字段(int64、float64、指针等)往前放,小字段(bool、byte、int16)往后挤。这不是风格偏好,是硬性空间优化。
结构体总大小必须是最大对齐值的倍数
即使所有字段都已按需对齐,结构体结尾仍可能追加 padding,目的是让整个结构体大小能被其最大字段对齐值整除。
例如含 int64 的结构体,最大对齐值是 8,那么 unsafe.Sizeof 结果一定是 8 的倍数:16、24、32……哪怕最后一个字段只占 1 字节,也得补够。
这个规则影响嵌套结构体:type Outer struct { x int32; y Inner },如果 Inner 内含 int64,那 Outer 的对齐值也会变成 8,进而影响外层 slice 或数组的内存布局。
cgo 场景下对齐不一致会直接崩溃
Go 默认对齐策略和 C 不同。C 头文件若用了 #pragma pack(1) 或 __attribute__((packed)),而 Go 端没做适配,读写同一块内存时就会越界或解析错位。
不能靠“试出来刚好能用”来判断兼容性。必须显式同步:
- 用
_ uint8手动补齐字段间隙 - 调整字段顺序使其与 C 端一致
- 在 cgo 注释中引入
// #include <stdalign.h></stdalign.h>并用alignas约束 - 用
unsafe.Offsetof或reflect.StructField.Offset运行时校验字段偏移是否匹配 C 定义
最容易被忽略的是:空 struct struct{} 虽然 unsafe.Sizeof 为 0,但它参与对齐计算——如果它前面字段结束在奇数地址,后面字段仍要跳到自己对齐值的倍数位置,padding 就这么悄悄产生了。











