Go 中沒有引用傳遞?
先說清楚,在 go 中沒有引用變量,所以更不存在什么引用傳值了。
什么是引用變量
在類 C++ 語言中,你可以聲明一個(gè)別名,給一個(gè)變量安上一個(gè)其他名字,我們把這稱為引用變量。
#include?
int?main()?{
?int?a?=?10;
?int?&b?=?a;
?int?&c?=?b;
?printf("%p?%p?%p\n",?&a,?&b,?&c);?//?0x7ffe114f0b14?0x7ffe114f0b14?0x7ffe114f0b14
?return?0;
}
你可以看到 a,b,c 都指向同一塊內(nèi)存地址,三者的值相同,當(dāng)你要在不同范圍內(nèi)聲明引用變量(即函數(shù)調(diào)用)時(shí),此功能很有用。
Go 中不存在引用變量
與 C++ 不同的是,Go 中的每一個(gè)變量都有著獨(dú)一無二的內(nèi)存地址。
package?main
import?"fmt"
func?main()?{
?var?a,?b,?c?int
?fmt.Println(&a,?&b,?&c)?//?0x1040a124?0x1040a128?0x1040a12c
}
你不可能在 Go 程序中找到兩個(gè)變量共享一塊內(nèi)存,但是可以讓兩個(gè)變量指向同一個(gè)內(nèi)存。
package?main
import?"fmt"
func?main()?{
?var?a?int
?var?b,?c?=?&a,?&a
?fmt.Println(b,?c)???//?0x1040a124?0x1040a124
?fmt.Println(&b,?&c)?//?0x1040c108?0x1040c110
}
在這個(gè)例子中,b 和 c 擁有 a 的地址,但是 b 和 c 這兩個(gè)變量卻被存儲(chǔ)在不同的內(nèi)存地址中,更改 b 的值并不會(huì)影響到 c。
map 和 channel 是引用嗎
不是,map 和 channel 都不是引用,如果他們是的話,下面這個(gè)例子就會(huì)輸出 false
package?main
import?"fmt"
func?fn(m?map[int]int)?{
?m?=?make(map[int]int)
}
func?main()?{
?var?m?map[int]int
?fn(m)
?fmt.Println(m?==?nil)
}
如果是引用變量的話,main 中的 m 被傳到 fn 中,那么經(jīng)過函數(shù)的處理 m 應(yīng)該已經(jīng)被初始化了才對(duì),但是可以看出 fn 的處理對(duì) m 并沒有影響,所以 map 也不是引用。
map 是一個(gè)指向 runtime.hmap 結(jié)構(gòu)的指針,如果你還有疑問的話,請(qǐng)繼續(xù)閱讀下去。
map 類型是什么
當(dāng)我們這樣聲明的時(shí)候。
m?:=?make(map[int]int)
編譯器將其替換為調(diào)用 map.go/makemap[1]
//?makemap?implements?Go?map?creation?for?make(map[k]v,?hint).
//?If?the?compiler?has?determined?that?the?map?or?the?first?bucket
//?can?be?created?on?the?stack,?h?and/or?bucket?may?be?non-nil.
//?If?h?!=?nil,?the?map?can?be?created?directly?in?h.
//?If?h.buckets?!=?nil,?bucket?pointed?to?can?be?used?as?the?first?bucket.
func?makemap(t?*maptype,?hint?int,?h?*hmap)*hmap
可以看到,makemap 函數(shù)返回 *hmap,一個(gè)指向hmap[2]結(jié)構(gòu)的指針,我們可以從 go 源碼中看到這些,除此之外,我們還可以證明 map 值的大小和 uintptr 一樣。
package?main
import?(
?"fmt"
?"unsafe"
)
func?main()?{
?var?m?map[int]int
?var?p?uintptr
?fmt.Println(unsafe.Sizeof(m),?unsafe.Sizeof(p))?//?8?8?(linux/amd64)
}
如果 map 是指針的話,它不應(yīng)該返回 *map[key]value 嗎
這是個(gè)好問題,為什么表達(dá)式 make(map[int]int) 返回一個(gè) map[int]int 類型的結(jié)構(gòu)?不應(yīng)該返回 *map[int]int 嗎?
Ian Taylor 在這個(gè)回答[3]中說:
In the very early days what we call maps now were written as pointers, so you wrote *map[int]int. We moved away from that when we realized that no one ever wrote
mapwithout writing*map.
所以說,Go 把 *map[int]int 重命名為 map[int]int
via: https://dave.cheney.net/2017/04/29/there-is-no-pass-by-reference-in-go
作者:Dave Cheney[4]譯者:Jun10ng[5]校對(duì):unknwon[6]
本文由 GCTT[7] 原創(chuàng)編譯,Go 中文網(wǎng)[8] 榮譽(yù)推出
參考資料
makemap: https://golang.org/src/runtime/map.go?h=makemap%28%29
[2]hmap: https://golang.org/src/runtime/map.go?h=hmap#L115
[3]在這個(gè)回答: https://groups.google.com/forum/#!msg/golang-nuts/SjuhSYDITm4/jnrp7rRxDQAJ
[4]Dave Cheney: https://dave.cheney.net/about
[5]Jun10ng: https://github.com/Jun10ng
[6]unknwon: https://github.com/unknwon
[7]GCTT: https://github.com/studygolang/GCTT
[8]Go 中文網(wǎng): https://studygolang.com/
推薦閱讀
