unsafe.sizeof(t) 返回类型 t 值的内存大小,unsafe.sizeof(*t) 返回指针本身大小(64 位系统为 8 字节);前者反映值布局,后者仅表示地址宽度,二者语义完全不同。

unsafe.Sizeof(T) 和 unsafe.Sizeof(*T) 的结果为什么不同
因为 unsafe.Sizeof 计算的是值本身的内存占用,不是类型定义或运行时对象开销。对任意类型 T,unsafe.Sizeof(T) 返回其值在内存中“平铺”所占字节数;而 unsafe.Sizeof(*T) 实际上是计算一个指向 T 的指针的大小——也就是机器字长:64 位系统上恒为 8,32 位系统上恒为 4。
常见错误现象:
- 把 unsafe.Sizeof(*T) 当成 “T 类型实例加一层指针后的总开销”,误以为它等于 unsafe.Sizeof(T) + 8;
- 在结构体嵌套指针字段时,用 unsafe.Sizeof 直接估算整个对象深度内存占用(它不递归)。
-
unsafe.Sizeof(int64(0))→8 -
unsafe.Sizeof(&int64(0))→8(64 位系统) -
unsafe.Sizeof(struct{ x int64; y *int64 }{})→16(含对齐,不是8+8=16那么简单,但确实不含 *int64 指向的目标内存)
什么时候该用 unsafe.Sizeof(T),什么时候该用 unsafe.Sizeof(*T)
看你要测的对象是不是“已经存在的值”。T 是值类型或结构体字面量,*T 是一个指针变量(哪怕还没解引用)。它们对应完全不同的使用场景:
- 想查某个 struct 实例在栈/堆上占多大——用
unsafe.Sizeof(myStruct) - 想确认当前平台指针宽度(比如做内存对齐判断、写底层序列化逻辑)——用
unsafe.Sizeof((*int)(nil))或更直白的unsafe.Sizeof(uintptr(0)) - 传入函数参数是
interface{},又想间接获取底层值大小?不行——unsafe.Sizeof对 interface{} 返回的是 interface 头部大小(2 个 word),和实际数据无关
结构体里有 *T 字段时,unsafe.Sizeof 的结果包含什么
只包含指针字段本身(即 8 字节),绝不包含它指向的任何内容。这是最容易误解的一点:很多人以为 unsafe.Sizeof 会“穿透”指针,其实它连字段名都不认识,只按结构体布局硬算字节。
示例:
蓝科外贸网站管理系统中英文双语版v1.8是针对外贸中小企业而开发的具有简单易用、功能强大,性价比高、扩展性好,安全性高、稳定性好的系统,可以加快外贸企业网站开发的速度和减少开发的成本。让不同的用户在懂的少许html语言的基础上,就能够快速的构建一个风格个性化的而功能强大的中英文企业网站。
立即学习“go语言免费学习笔记(深入)”;
type S struct {
A int64
B *int64
}
s := S{A: 1, B: new(int64)}
fmt.Println(unsafe.Sizeof(s)) // 输出 16(64 位系统),不是 16 + 8- 字段
B贡献的是一个指针宽度,不是*int64类型的unsafe.Sizeof(int64(0)) - 如果
B是[1000]int64,unsafe.Sizeof(s)会跳到8 + 8000 = 8008;但换成*[1000]int64,就又回到16 - 注意结构体填充(padding):字段顺序、对齐要求会影响最终大小,不能简单加总
unsafe.Sizeof 的结果在不同 Go 版本或 GOARCH 下稳定吗
对同一 GOARCH,结果是稳定的;但跨架构或跨版本可能变——尤其是涉及结构体时。Go 不保证结构体字段布局,只保证导出字段的 ABI 兼容性(且仅限于相同 GOOS/GOARCH 下的同一 major 版本)。所以:
-
unsafe.Sizeof(int(0))在 64 位系统上始终是8(int是 64 位),但在 32 位系统上是4 -
unsafe.Sizeof(struct{a byte; b uint64}{})在 amd64 上是16(因对齐),在 arm64 上也通常是16,但你不该依赖这个数字做跨平台逻辑 - 别把
unsafe.Sizeof结果写死进配置或协议定义里——它不是 ABI 承诺的一部分
真正容易被忽略的是:哪怕你只跑在一个架构上,一旦结构体字段增减、重排、加 tag(比如 //go:notinheap),unsafe.Sizeof 就可能突变。它反映的是编译时快照,不是语义契约。









