久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長資訊網
      最全最豐富的資訊網站

      快看!用Go struct不能犯的一個低級錯誤!

      下面由golang教程欄目給大家介紹用 Go struct 不能犯的一個低級錯誤,希望對需要的朋友有所幫助!

      用 Go struct 不能犯的一個低級錯誤!

      本文 GitHub github.com/eddycjy/blog 已收錄

      大家好,我是煎魚。

      前段時間我分享了 《手撕 Go 面試官:Go 結構體是否可以比較,為什么?》的文章,把基本 Go struct 的比較依據研究了一番。這不,最近有一位讀者,遇到了一個關于 struct 的新問題,不得解。

      大家一起來看看,建議大家在看到代碼例子后先思考一下答案,再往下看。

      獨立思考很重要。

      疑惑的例子

      其給出的例子一如下:

      type People struct {}  func main() {  a := &People{}  b := &People{}  fmt.Println(a == b) }

      你認為輸出結果是什么呢?

      輸出結果是:false。

      再稍加改造一下,例子二如下:

      type People struct {}  func main() {  a := &People{}  b := &People{}  fmt.Printf("%pn", a)  fmt.Printf("%pn", b)  fmt.Println(a == b) }

      輸出結果是:true。

      他的問題是 "為什么第一個返回 false 第二個返回 true,是什么原因導致的?

      煎魚進一步的精簡這個例子,得到最小示例:

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

      輸出結果:

      // a, b; a == b 0xc00005cf57 0xc00005cf57 false  // c, d &{} &{} // c, d, c == d 0x118c370 0x118c370 true

      第一段代碼的結果是 false,第二段的結果是 true,且可以看到內存地址指向的完全一樣,也就是排除了輸出后變量內存指向改變導致的原因。

      進一步來看,似乎是 fmt.Print 方法導致的,但一個標準庫里的輸出方法,會導致這種奇怪的問題?

      問題剖析

      如果之前有被這個 “坑” 過,或有看過源碼的同學??赡苣軌蚩焖俚囊庾R到,導致這個輸出是逃逸分析所致的結果。

      我們對例子進行逃逸分析:

      // 源代碼結構 $ cat -n main.go      5    func main() {      6        a := new(struct{})      7        b := new(struct{})      8        println(a, b, a == b)      9         10        c := new(struct{})     11        d := new(struct{})     12        fmt.Println(c, d)     13        println(c, d, c == d)     14    }  // 進行逃逸分析 $ go run -gcflags="-m -l" main.go # command-line-arguments ./main.go:6:10: a does not escape ./main.go:7:10: b does not escape ./main.go:10:10: c escapes to heap ./main.go:11:10: d escapes to heap ./main.go:12:13: ... argument does not escape

      通過分析可得知變量 a, b 均是分配在棧中,而變量 c, d 分配在堆中。

      其關鍵原因是因為調用了 fmt.Println 方法,該方法內部是涉及到大量的反射相關方法的調用,會造成逃逸行為,也就是分配到堆上。

      為什么逃逸后相等

      關注第一個細節(jié),就是 “為什么逃逸后,兩個空 struct 會是相等的?”。

      這里主要與 Go runtime 的一個優(yōu)化細節(jié)有關,如下:

      // runtime/malloc.go var zerobase uintptr

      變量 zerobase 是所有 0 字節(jié)分配的基礎地址。更進一步來講,就是空(0字節(jié))的在進行了逃逸分析后,往堆分配的都會指向 zerobase 這一個地址。

      所以空 struct 在逃逸后本質上指向了 zerobase,其兩者比較就是相等的,返回了 true。

      為什么沒逃逸不相等

      關注第二個細節(jié),就是 “為什么沒逃逸前,兩個空 struct 比較不相等?”。

      快看!用Go struct不能犯的一個低級錯誤!

      從 Go spec 來看,這是 Go 團隊刻意而為之的設計,不希望大家依賴這一個來做判斷依據。如下:

      This is an intentional language choice to give implementations flexibility in how they handle pointers to zero-sized objects. If every pointer to a zero-sized object were required to be different, then each allocation of a zero-sized object would have to allocate at least one byte. If every pointer to a zero-sized object were required to be the same, it would be different to handle taking the address of a zero-sized field within a larger struct.

      還說了一句很經典的,細品:

      Pointers to distinct zero-size variables may or may not be equal.

      另外空 struct 在實際使用中的場景是比較少的,常見的是:

      • 設置 context,傳遞時作為 key 時用到。
      • 設置空 struct 業(yè)務場景中臨時用到。

      但業(yè)務場景的情況下,也大多數會隨著業(yè)務發(fā)展而不斷改變,假設有個遠古時代的 Go 代碼,依賴了空 struct 的直接判斷,豈不是事故上身?

      不可直接依賴

      因此 Go 團隊這番操作,與 Go map 的隨機性如出一轍,避免大家對這類邏輯的直接依賴,是值得思考的。

      而在沒逃逸的場景下,兩個空 struct 的比較動作,你以為是真的在比較。實際上已經在代碼優(yōu)化階段被直接優(yōu)化掉,轉為了 false。

      因此,雖然在代碼上看上去是 == 在做比較,實際上結果是 a == b 時就直接轉為了 false,比都不需要比了。

      你說妙不?

      沒逃逸讓他相等

      既然我們知道了他是在代碼優(yōu)化階段被優(yōu)化的,那么相對的,知道了原理的我們也可以借助在 go 編譯運行時的 gcflags 指令,讓他不優(yōu)化。

      在運行前面的例子時,執(zhí)行 -gcflags="-N -l" 指令:

      $ go run -gcflags="-N -l" main.go  0xc000092f06 0xc000092f06 true &{} &{} 0x118c370 0x118c370 true

      你看,兩個比較的結果都是 true 了。

      總結

      在今天這篇文章中,我們針對 Go 語言中的空結構體(struct)的比較場景進行了進一步的補全。經過這兩篇文章的洗禮,你會更好的理解 Go 結構體為什么叫既可比較又不可比較了。

      而空結構比較的奇妙,主要原因如下:

      • 若逃逸到堆上,空結構體則默認分配的是 runtime.zerobase 變量,是專門用于分配到堆上的 0 字節(jié)基礎地址。因此兩個空結構體,都是 runtime.zerobase,一比較當然就是 true 了。
      • 若沒有發(fā)生逃逸,也就分配到棧上。在 Go 編譯器的代碼優(yōu)化階段,會對其進行優(yōu)化,直接返回 false。并不是傳統(tǒng)意義上的,真的去比較了。

      不會有人拿來出面試題,不會吧,為什么 Go 結構體說可比較又不可比較?

      若有任何疑問歡迎評論區(qū)反饋和交流,最好的關系是互相成就,各位的點贊就是煎魚創(chuàng)作的最大動力,感謝支持。

      文章持續(xù)更新,可以微信搜【腦子進煎魚了】閱讀,回復【000】有我準備的一線大廠面試算法題解和資料;本文 GitHub github.com/eddycjy/blog 已收錄,歡迎 Star 催更。

      贊(0)
      分享到: 更多 (0)
      ?
      網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號