真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

使用Gostruct的注意事項(xiàng)

這篇文章給大家分享的是有關(guān)使用Go struct的注意事項(xiàng)的內(nèi)容。小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過(guò)來(lái)看看吧。

成都網(wǎng)絡(luò)公司-成都網(wǎng)站建設(shè)公司成都創(chuàng)新互聯(lián)公司10多年經(jīng)驗(yàn)成就非凡,專業(yè)從事成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站,成都網(wǎng)頁(yè)設(shè)計(jì),成都網(wǎng)頁(yè)制作,軟文發(fā)布平臺(tái)一元廣告等。10多年來(lái)已成功提供全面的成都網(wǎng)站建設(shè)方案,打造行業(yè)特色的成都網(wǎng)站建設(shè)案例,建站熱線:18982081108,我們期待您的來(lái)電!

疑惑的例子

其給出的例子一如下:

type People struct {}

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

你認(rèn)為輸出結(jié)果是什么呢?

輸出結(jié)果是:false。

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

type People struct {}

func main() {
 a := &People{}
 b := &People{}
 fmt.Printf("%p\n", a)
 fmt.Printf("%p\n", b)
 fmt.Println(a == b)
}

輸出結(jié)果是:true。

他的問(wèn)題是 "為什么第一個(gè)返回 false 第二個(gè)返回 true,是什么原因?qū)е碌?/strong>?

煎魚(yú)進(jìn)一步的精簡(jiǎn)這個(gè)例子,得到最小示例:

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)
}

輸出結(jié)果:

// a, b; a == b
0xc00005cf57 0xc00005cf57 false

// c, d
&{} &{}
// c, d, c == d
0x118c370 0x118c370 true

第一段代碼的結(jié)果是 false,第二段的結(jié)果是 true,且可以看到內(nèi)存地址指向的完全一樣,也就是排除了輸出后變量?jī)?nèi)存指向改變導(dǎo)致的原因。

進(jìn)一步來(lái)看,似乎是 fmt.Print 方法導(dǎo)致的,但一個(gè)標(biāo)準(zhǔn)庫(kù)里的輸出方法,會(huì)導(dǎo)致這種奇怪的問(wèn)題?

問(wèn)題剖析

如果之前有被這個(gè) “坑” 過(guò),或有看過(guò)源碼的同學(xué)??赡苣軌蚩焖俚囊庾R(shí)到,導(dǎo)致這個(gè)輸出是逃逸分析所致的結(jié)果。

我們對(duì)例子進(jìn)行逃逸分析:

// 源代碼結(jié)構(gòu)
$ 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    }

// 進(jìn)行逃逸分析
$ 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

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

其關(guān)鍵原因是因?yàn)檎{(diào)用了 fmt.Println 方法,該方法內(nèi)部是涉及到大量的反射相關(guān)方法的調(diào)用,會(huì)造成逃逸行為,也就是分配到堆上。

為什么逃逸后相等

關(guān)注第一個(gè)細(xì)節(jié),就是 “為什么逃逸后,兩個(gè)空 struct 會(huì)是相等的?”。

這里主要與 Go runtime 的一個(gè)優(yōu)化細(xì)節(jié)有關(guān),如下:

// runtime/malloc.go
var zerobase uintptr

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

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

為什么沒(méi)逃逸不相等

關(guān)注第二個(gè)細(xì)節(jié),就是 “為什么沒(méi)逃逸前,兩個(gè)空 struct 比較不相等?”。

從 Go spec 來(lái)看,這是 Go 團(tuán)隊(duì)刻意而為之的設(shè)計(jì),不希望大家依賴這一個(gè)來(lái)做判斷依據(jù)。如下:

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.

還說(shuō)了一句很經(jīng)典的,細(xì)品:

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

另外空 struct 在實(shí)際使用中的場(chǎng)景是比較少的,常見(jiàn)的是:

  • 設(shè)置 context,傳遞時(shí)作為 key 時(shí)用到。

  • 設(shè)置空 struct 業(yè)務(wù)場(chǎng)景中臨時(shí)用到。

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

不可直接依賴

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

而在沒(méi)逃逸的場(chǎng)景下,兩個(gè)空 struct 的比較動(dòng)作,你以為是真的在比較。實(shí)際上已經(jīng)在代碼優(yōu)化階段被直接優(yōu)化掉,轉(zhuǎn)為了 false。

因此,雖然在代碼上看上去是 == 在做比較,實(shí)際上結(jié)果是 a == b 時(shí)就直接轉(zhuǎn)為了 false,比都不需要比了。

你說(shuō)妙不?

沒(méi)逃逸讓他相等

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

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

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

你看,兩個(gè)比較的結(jié)果都是 true 了。

總結(jié)

在今天這篇文章中,我們針對(duì) Go 語(yǔ)言中的空結(jié)構(gòu)體(struct)的比較場(chǎng)景進(jìn)行了進(jìn)一步的補(bǔ)全。經(jīng)過(guò)這兩篇文章的洗禮,你會(huì)更好的理解 Go 結(jié)構(gòu)體為什么叫既可比較又不可比較了。

而空結(jié)構(gòu)比較的奇妙,主要原因如下:

  • 若逃逸到堆上,空結(jié)構(gòu)體則默認(rèn)分配的是 runtime.zerobase 變量,是專門用于分配到堆上的 0 字節(jié)基礎(chǔ)地址。因此兩個(gè)空結(jié)構(gòu)體,都是 runtime.zerobase,一比較當(dāng)然就是 true 了。

  • 若沒(méi)有發(fā)生逃逸,也就分配到棧上。在 Go 編譯器的代碼優(yōu)化階段,會(huì)對(duì)其進(jìn)行優(yōu)化,直接返回 false。并不是傳統(tǒng)意義上的,真的去比較了。

感謝各位的閱讀!關(guān)于“使用Go struct的注意事項(xiàng)”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!


網(wǎng)站題目:使用Gostruct的注意事項(xiàng)
文章轉(zhuǎn)載:http://weahome.cn/article/pjijsh.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部