struct{}在Go中大小为0字节,unsafe.Sizeof(struct{}{})返回0;它不占内存、可共享地址,但地址相等不可靠,仅适用于存在性表达而非值比较或标识。

struct{} 的零字节特性决定了它没有独立存储需求
空结构体 struct{} 在 Go 中大小为 0,unsafe.Sizeof(struct{}{}) 返回 0。它不携带任何字段、不需保存状态、也不依赖运行时分配——编译器知道“这个值根本不需要内存”,于是所有 struct{} 实例可共享同一地址(通常是地址 0x0 或某个固定只读地址),只要语义上不暴露可变性即可。
Go 运行时对零大小类型的特殊处理逻辑
Go 编译器和运行时对零大小类型(ZST, Zero-Sized Type)有明确约定:不为其单独分配堆/栈空间,也不在 map、slice、channel 等容器中预留 value 存储位。例如:
-
map[string]struct{}的底层 bucket 中,value 区域完全省略,只存 key 和 hash -
chan struct{}的缓冲区不存任何数据,只用作信号同步,发送/接收操作不拷贝值 - 数组
[100]struct{}占用 0 字节,&a[0]和&a[99]可能指向同一地址(取决于编译器实现,但行为是定义良好的)
为什么不能靠地址相等判断两个 struct{} 是否“同一个”
虽然多个 struct{} 值常共享地址,但这属于实现细节,不是语言规范保证的行为。以下情况会打破地址一致性:
- 作为结构体字段嵌入时:
struct{ x int; y struct{} }中的y字段仍占 0 字节,但其偏移由对齐规则决定,可能被“挤”到非零偏移处(尽管值本身无内容) - 取地址后逃逸到堆上:若编译器判定需显式分配(如闭包捕获、反射操作),可能为每个实例分配独立(但内容为空)的栈/堆空间
- 不同包或不同编译单元中,运行时可能选择不同固定地址(如 runtime.zerobase)
所以 fmt.Printf("%p", &s) 输出相同,不代表逻辑上“是同一个对象”;== 比较永远为 true,但地址相等不可用于标识或缓存键。
实际编码中容易忽略的关键点
用 struct{} 做 channel 元素或 map value 很高效,但别把它当“单例指针”来用。真正需要唯一地址标识的场景(如全局哨兵值),应显式定义变量并取其地址:var sentinel = struct{}{},再用 &sentinel —— 这才是稳定、可移植、符合直觉的做法。


















