unsafe.sizeof返回的是类型值本身占用的内存字节数,已包含字段对齐填充,但不包括动态分配内存;结果为编译期常量,受字段顺序、平台架构和对齐规则影响。

unsafe.Sizeof 返回的是什么,不是字节对齐后的大小
unsafe.Sizeof 返回的是该类型**值本身占用的内存字节数**,不包括字段对齐填充、不反映结构体整体对齐要求。它只看“这个值放进去要占多少字节”,比如 struct{a uint8; b uint32} 中,a 占 1 字节,b 占 4 字节,但中间有 3 字节填充(为满足 b 的 4 字节对齐),所以 unsafe.Sizeof 返回的是 8,而不是 5。
常见误解是把它当“紧凑大小”用——实际它已经把填充算进去了,只是没告诉你填充在哪。想看字段偏移和填充细节,得配合 unsafe.Offsetof 和 unsafe.Alignof。
struct 字段顺序直接影响 Sizeof 结果
Go 编译器不会重排 struct 字段,字段声明顺序 = 内存布局顺序。所以调整字段顺序能显著改变 unsafe.Sizeof 的结果,尤其在混合大小类型时:
-
struct{a uint8; b uint64; c uint16}→ 填充多,unsafe.Sizeof很可能为 24 -
struct{b uint64; c uint16; a uint8}→ 填充少,可能压到 16
验证方式很简单:
package main
import (
"fmt"
"unsafe"
)
func main() {
type A struct{ a uint8; b uint64; c uint16 }
type B struct{ b uint64; c uint16; a uint8 }
fmt.Println(unsafe.Sizeof(A{})) // 输出 24
fmt.Println(unsafe.Sizeof(B{})) // 输出 16
}
不能对 interface{} 或 nil 指针直接调用 unsafe.Sizeof
unsafe.Sizeof 接收的是**表达式值**,不是类型名。传 interface{} 变量时,它返回的是接口头(2 个指针大小,通常 16 字节),不是底层值的大小;传 nil 指针会 panic(因为求值失败)。
- 错:
unsafe.Sizeof((*int)(nil))→ panic: invalid memory address - 对:
unsafe.Sizeof(*p)(其中p是非 nil 的*int) - 若只想查
int类型大小,写unsafe.Sizeof(int(0))或unsafe.Sizeof(*new(int))
切片、map、channel 等引用类型同理:它们的 unsafe.Sizeof 固定为头结构大小(如 slice 是 24 字节),与底层数组长度无关。
Sizeof 在跨平台或不同 Go 版本下可能变化
虽然 Go 规范保证了基本类型的大小(如 int 在 64 位系统上通常是 8 字节),但 unsafe.Sizeof 对 struct 的结果依赖于编译器的具体对齐策略。例如:
- 某些旧版 Go 对小字段组合的填充更激进
- ARM64 和 amd64 的默认对齐约束略有差异
- 启用
-gcflags="-d=checkptr"不影响Sizeof,但会影响运行时对指针运算的检查
因此,不要把 unsafe.Sizeof 的结果硬编码进序列化逻辑或 C 互操作的 struct 定义中——应始终用 unsafe.Offsetof 配合字段校验,或用 go tool cgo -godefs 生成可靠绑定。
真正容易被忽略的,是字段对齐规则和平台耦合性:同一段 struct 定义,在本地调试时 Sizeof 是 32,上线到容器里跑着跑着变成 40,往往是因为镜像基础镜像用了 musl libc 或不同架构的 builder。











