为何新增打印语句后Go语言结构体指针比较结果变为true?
问题:空结构体指针比较结果随代码变更异常变化
我编写了如下Go语言代码:
package main import "fmt" type MyStruct struct { } func main() { a := &MyStruct{} b := &MyStruct{} fmt.Println(a == b) fmt.Println(*a == *b) }
输出符合预期:
false true
但当我在末尾新增两句打印语句后:
package main import "fmt" type MyStruct struct { } func main() { a := &MyStruct{} b := &MyStruct{} fmt.Println(a == b) fmt.Println(*a == *b) fmt.Println(&a) fmt.Println(&b) }
输出变为:
true true 0xc0000ae018 0xc0000ae020
为何此时第一行的指针比较结果变为true?
解答
核心原因:Go编译器对空结构体的内存优化
- 空结构体的特性:
struct{}(空结构体)的大小为0,它的实例不占用任何内存空间。所有空结构体实例在逻辑上完全等价,没有任何区别。 - 编译器的指针复用优化:Go编译器会针对空结构体做特殊优化——当代码中创建多个指向空结构体的指针时,编译器会尽可能让这些指针指向同一个内存地址。因为空结构体没有字段,复用地址不会影响程序逻辑,还能减少内存开销。不过这种优化的触发会依赖代码上下文,并非固定触发。
两个示例的差异分析
第一个示例(无新增打印)
仅创建两个空结构体指针并直接比较,此时编译器可能未触发指针复用优化,a和b指向了不同的(无实际意义的)内存地址,因此a == b返回false。第二个示例(新增打印&a、&b)
新增的fmt.Println(&a)和fmt.Println(&b)打印的是指针变量a和b自身的内存地址(而非它们指向的空结构体地址)。此时编译器的优化策略调整,判定可以安全复用空结构体的指针地址,于是让a和b指向了同一个地址,导致a == b返回true。
验证方式
如果在第二个示例中添加一行打印a和b本身值的代码:
fmt.Println(a, b)
会输出两个完全相同的地址,直接印证了指针被复用的结论。
内容的提问来源于stack exchange,提问作者naneri
相关产品推荐
相关产品推荐

