Go中空结构体为何不占内存?作为结构体字段时内存占用为何有差异?
关于Go中空结构体的内存占用与对齐问题
1. 空结构体struct{}为何不占用内存?变量如何存储?
- Go语言规范明确空结构体的大小为0,因为它没有任何字段,不需要存储任何数据。
- 对于
a := struct{}{}这类变量,编译器会做特殊优化:栈上不会为它分配实际的存储空间。编译阶段就会把它的引用直接处理掉,运行时不存在对应的栈内存。它更像是一个语法层面的“占位符”,仅用于满足类型要求,无需实际存储。
2. 空结构体作为结构体字段时是否占用内存?
空结构体本身大小为0,但作为结构体字段时,是否会增加整个结构体的大小,完全取决于内存对齐规则和字段排列顺序。结合你给出的实验结果分析:
实验代码
type EmptyStruct struct {} type st1 struct { a int64 } type st2 struct { a int64 b struct{} } type st3 struct { b struct{} a int64 } func main() { const format string = "%-3s: size: %-2d, val: %v\n" es := EmptyStruct{} s1 := st1{} s2 := st2{} s3 := st3{} fmt.Printf(format, "es", unsafe.Sizeof(es), es) fmt.Printf(format, "s1", unsafe.Sizeof(s1), s1) fmt.Printf(format, "s2", unsafe.Sizeof(s2), s2) fmt.Printf("\t"+format, "s2.a", unsafe.Sizeof(s2.a), s2.a) fmt.Printf("\t"+format, "s2.b", unsafe.Sizeof(s2.b), s2.b) fmt.Printf(format, "s3", unsafe.Sizeof(s3), s3) fmt.Printf("\t"+format, "s3.a", unsafe.Sizeof(s3.a), s3.a) fmt.Printf("\t"+format, "s3.b", unsafe.Sizeof(s3.b), s3.b) }
实验输出
es : size: 0 , val: {} s1 : size: 8 , val: {0} s2 : size: 16, val: {0 {}} s2.a: size: 8 , val: 0 s2.b: size: 0 , val: {} s3 : size: 8 , val: {{} 0} s3.a: size: 8 , val: 0 s3.b: size: 0 , val: {}
结果分析
st1仅包含int64类型字段,大小为8字节,和int64本身的大小一致。st2字段顺序为int64 a+struct{} b:
Go的内存对齐规则要求,结构体的总大小必须是其最大字段对齐边界的整数倍。int64的对齐边界是8字节,当a占满8字节后,空结构体b虽然本身不占空间,但为了让整个结构体满足对齐要求,编译器会自动填充8字节,最终总大小为16字节(8的2倍)。st3字段顺序为struct{} b+int64 a:
空结构体b被放在开头,编译器会直接把它“合并”到后续int64 a的对齐空间中——因为空结构体不需要实际存储,所以整个结构体的大小就是int64的8字节,无需额外填充。
验证内存对齐的方法
可以通过unsafe.Offsetof查看字段的内存偏移量,在main函数中添加以下代码:
fmt.Printf("s2.a offset: %d, s2.b offset: %d\n", unsafe.Offsetof(s2.a), unsafe.Offsetof(s2.b)) fmt.Printf("s3.b offset: %d, s3.a offset: %d\n", unsafe.Offsetof(s3.b), unsafe.Offsetof(s3.a))
运行后会得到如下输出:
s2.a offset: 0, s2.b offset: 8 s3.b offset: 0, s3.a offset: 0
这个结果直接验证了:
s2中b的偏移量是8,紧跟在a之后,而结构体需要对齐到8字节倍数,所以总大小为16字节(含8字节填充)。s3中a和b的偏移量都是0,二者共享同一内存位置(因为空结构体无需存储),所以总大小为8字节。
内容的提问来源于stack exchange,提问作者AquaOwO
相关产品推荐
相关产品推荐

