日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

惊!空 struct 地址竟然不相等

發布時間:2024/4/11 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 惊!空 struct 地址竟然不相等 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Go 語言里的空 struct{} 是一個特殊的結構,因為編譯器優化的關系,會導致我們拿空 struct 指針做比較的時候出現一些意外的結果。之前有人提過相關的 issue,不過還是值得我們簡單研究一番。

官方 spec 中對此的描述是:"Pointers to distinct zero-size variables may or may not be equal.",來看看實現上具體為什么會是這樣的。

package mainimport "fmt"func main() {a := new(struct{})b := new(struct{})println(a, b, a == b)c := new(struct{})d := new(struct{})fmt.Println(c, d, c == d) }

結果可能讓你很驚訝:

0xc00007af1f 0xc00007af1f false &{} &{} true

如果之前碰到過給程序加上 fmt.Println 就導致結果變化的情況的同學,可能會想到這里的原因,是和逃逸分析相關。(可以參考這兩個 issue :8618 和 31317)

沒錯:

~/test git:master ??? cat -n true.go ? ?1 package main23 import "fmt"45 func main() {6 a := new(struct{})7 b := new(struct{})8 println(a, b, a == b)910 c := new(struct{})11 d := new(struct{})12 fmt.Println(c, d, c == d)13 } ~/test git:master ??? go run -gcflags="-m" true.go# command-line-arguments ./true.go:12:13: inlining call to fmt.Println ./true.go:6:10: main new(struct {}) does not escape ./true.go:7:10: main new(struct {}) does not escape ./true.go:10:10: new(struct {}) escapes to heap ./true.go:11:10: new(struct {}) escapes to heap ./true.go:12:13: c escapes to heap ./true.go:12:13: d escapes to heap ./true.go:12:22: c == d escapes to heap ./true.go:12:13: main []interface {} literal does not escape ./true.go:12:13: io.Writer(os.Stdout) escapes to heap <autogenerated>:1: (*File).close .this does not escape 0xc00007af1f 0xc00007af1f false &{} &{} true

在 fmt.Println 中單獨出現了 c,所以 c = new 的時候就發生了逃逸,d 也同理:

  • 未逃逸:a,b

  • 逃逸:c,d

看過一點 go runtime 代碼的同學應該知道,在 runtime 里有這么個東西:

// base address for all 0-byte allocations var zerobase uintptr

0 大小的內存分配均會指向該變量地址,所謂的 0 字節,主要就是這種空 struct。不過這里沒說清楚的是,只有在堆上分配的 0 大小的 struct 才會指向該地址:

package mainimport "fmt"var a = new(struct{}) // 堆上func main() {var b = new(struct{})fmt.Println(b/*這里單獨打印了,所以 b escape 到堆上*/, a == b) }

那么空 struct 逃逸之后其實都是同一個變量,地址相等,我們是可以理解了。

沒逃逸的 a 和 b 為什么地址不相等呢?這里可以祭出 SSA 大法:

~/test git:master ??? sudo GOSSAFUNC=main go build true.go

可以看到這個 false,其實是在代碼優化階段(opt)直接被編譯后端作為常量優化掉了,直接轉成了 false。

所以我們本能地以為 == 是在做指針比較,但是常量優化會替我們做出不那么符合直覺的判斷,直接把 a == b rewrite 為 false。

問題到這里也就結束了,既然我們知道這里是優化階段做的,那是不是我們把優化關閉就會導致輸出 true 呢?

確實是的:

~/test git:master ??? cat true.gopackage mainimport "fmt"func main() {a := new(struct{})b := new(struct{})println(a, b, a == b)c := new(struct{})d := new(struct{})fmt.Println(c, d, c == d) }~/test git:master ??? go run -gcflags="-N -l" true.go0xc00007aece 0xc00007aece true &{} &{} true

我們可以用 Go 附帶的 SSA 工具去更深入地認識整個編譯階段干的事情,從而理解官方的所謂“實現細節”和 spec 上的 may or may not 到底是怎么回事。

只要知道怎么使用工具,結論反而也就沒那么重要了。

[1]?https://github.com/golang/go/issues/8618

[2] https://github.com/golang/go/issues/31317

總結

以上是生活随笔為你收集整理的惊!空 struct 地址竟然不相等的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。