规格:比较运算符: https://golang.org/ref/spec#Comparison_operators
指针值具有可比性。如果两个指针值指向同一个变量或者都具有值,则它们相等nil
. 指向不同的指针零尺寸 https://golang.org/ref/spec#Size_and_alignment_guarantees变量可能相等也可能不相等。
并且规格:尺寸和对齐保证: https://golang.org/ref/spec#Size_and_alignment_guarantees
如果结构体或数组类型不包含大小大于零的字段(或元素),则其大小为零。两个不同的零大小变量may在内存中具有相同的地址。
尺寸s
and ss
变量为零,所以&s
and &ss
是指向不同的零大小变量的指针,因此规范不保证它们的相等性。这意味着什么&s == &ss
可以评估为true
or false
,你不能指望结果会是什么,这样做是错误的。
不过,奇怪的是,在应用程序的单个运行时期间,它们一次相等,一次不相等。教训是永远不要依赖它。
不同的行为可以通过查看逃逸分析来解释。
让我们将您的应用程序简化为:
var s, ss struct{} // two empty structs
arr1 := [6]*struct{}{&s} // array with empty struct pointer
arr2 := [6]*struct{}{&ss} // array with empty struct pointer
fmt.Println(&s == &ss, arr1 == arr2) // false, true
运行逃逸分析go run -gcflags '-m' play.go
gives:
./play.go:13:17: &s == &ss escapes to heap
./play.go:13:30: arr1 == arr2 escapes to heap
./play.go:11:23: main &s does not escape
./play.go:12:23: main &ss does not escape
./play.go:13:14: main &s does not escape
./play.go:13:20: main &ss does not escape
./play.go:13:13: main ... argument does not escape
false true
&s
and &ss
不要逃避(因为它们没有传递给fmt.Println()
,只有结果&s == &ss
).
如果我们向上面的简化应用程序添加一行:
var s, ss struct{} // two empty structs
arr1 := [6]*struct{}{&s} // array with empty struct pointer
arr2 := [6]*struct{}{&ss} // array with empty struct pointer
fmt.Println(&s == &ss, arr1 == arr2) // true, true
fmt.Printf("%p %p\n", &s, &ss) // true, true
运行逃逸分析现在给出:
./play.go:13:17: &s == &ss escapes to heap
./play.go:13:30: arr1 == arr2 escapes to heap
./play.go:15:24: &s escapes to heap
./play.go:15:24: &s escapes to heap
./play.go:10:6: moved to heap: s
./play.go:15:28: &ss escapes to heap
./play.go:15:28: &ss escapes to heap
./play.go:10:9: moved to heap: ss
./play.go:11:23: main &s does not escape
./play.go:12:23: main &ss does not escape
./play.go:13:14: main &s does not escape
./play.go:13:20: main &ss does not escape
./play.go:13:13: main ... argument does not escape
./play.go:15:12: main ... argument does not escape
true true
行为发生了变化:我们现在看到true true
输出(尝试在去游乐场 https://play.golang.org/p/6aGn3RZTmL2).
行为改变的原因是因为&s
and &ss
逃逸到堆:它们被直接传递到fmt.Println()
,因此编译器改变了它们的存储方式(位置),随之而来的是它们的地址。
查看相关/可能的重复:空结构切片的 Golang 地址 https://stackoverflow.com/questions/48052722/golang-address-of-slices-of-empty-structs/48053270#48053270