为何添加fmt.Printf会改变Go中持有空结构体的接口变量相等性结果?
为什么fmt.Printf会影响Go语言接口的比较结果?
环境
go version go1.25.6 darwin/amd64
带fmt.Printf的测试代码
package main import "fmt" type Animal interface { Name() string } type Cat struct { } func (c Cat) Name() string { return "cat" } func main() { var c1 Animal = new(Cat) var c2 Animal = new(Cat) fmt.Println(c1 == c2) fmt.Printf("%p,%p", c1, c2) }
输出
true 0x56d83c0,0x56d83c0
注释fmt.Printf后的测试代码
package main import "fmt" type Animal interface { Name() string } type Cat struct { } func (c Cat) Name() string { return "cat" } func main() { var c1 Animal = new(Cat) var c2 Animal = new(Cat) fmt.Println(c1 == c2) // fmt.Printf("%p,%p", c1, c2) }
输出
false
原因解析
这是Go语言编译器的逃逸分析和接口值结构共同作用的结果。
Go的接口值包含两部分:类型指针和数据指针。只有当两个接口值的类型指针和数据指针完全相等时,比较结果才为true。
- 当代码中存在
fmt.Printf("%p", c1)时,%p格式化符会读取接口的底层数据指针,这会触发编译器将new(Cat)创建的Cat实例分配到堆上。为了优化,编译器会复用两次new(Cat)的堆内存地址,导致c1和c2的底层数据指针完全相同,接口比较结果为true。 - 注释掉
fmt.Printf后,编译器的逃逸分析判断Cat实例不需要逃逸到堆上,会将它们分配到栈上。栈上两次new(Cat)会生成两个独立的内存地址,此时c1和c2的底层数据指针不同,接口比较结果自然为false。
简单来说:打印操作强制让实例逃到堆上并被复用,不打印时实例在栈上独立分配,导致接口数据指针不同,最终让比较结果反转。
内容的提问来源于stack exchange,提问作者leesea
相关产品推荐
相关产品推荐

