在 Go
中函數傳遞參數有傳值和傳指針兩種類型,本文將從細節之處剖析二者的不一樣。json
先看一個demo
:函數
package main import ( "encoding/json" "fmt" ) type Girl struct { Name string `json:"name"` DressColor string `json:"dress_color"` } func (g Girl) SetColor(color string) { g.DressColor = color } func (g Girl) GetJson() string { data, _ := json.Marshal(&g) return string(data) } func main() { g := Girl{Name: "yueyue"} g.SetColor("white") fmt.Println(g.GetJson()) }
將打印出什麼結果呢?spa
將輸出:指針
{"name":"yueyue","dress_color":""}
咦,爲啥顏色沒有設置成功?code
仔細思考,原來就是今天要分析的 Golang
中關於函數傳值與傳指針的區別沒搞清楚。首先,咱們看到 SetColor
和 GetJson
函數都是值傳遞,因此實際在 main
中調用 g.SetColor
的時候,是拷貝了一份副本給函數 SetColor
,而後在函數內對副本進行了 color
的設置;但實際上此時原來的 g
對象卻依然只有 name
屬性,因此輸出了以上結果。對象
咱們來打印一下傳值先後對應的 g
是否是同一個對象就知道了:內存
package main import ( "encoding/json" "fmt" ) type Girl struct { Name string `json:"name"` DressColor string `json:"dress_color"` } func (g Girl) SetColor(color string) { fmt.Printf("g1: %p\n", &g) g.DressColor = color } func (g Girl) GetJson() string { data, _ := json.Marshal(&g) return string(data) } func main() { g := Girl{Name: "yueyue"} fmt.Printf("g0: %p\n", &g) g.SetColor("white") fmt.Println(g.GetJson()) }
輸出結果:rem
g0: 0xc42000a060g1: 0xc42000a080string
{"name":"yueyue","dress_color":""}it
發現確實 g0
與 g1
對象的內存地址是不一樣的,說明是兩個不一樣的對象。接下來,咱們看一下傳指針的狀況:
package main import ( "encoding/json" "fmt" ) type Girl struct { Name string `json:"name"` DressColor string `json:"dress_color"` } func (g *Girl) SetColor(color string) { fmt.Printf("g1: %p\n", g) g.DressColor = color } func (g *Girl) GetJson() string { data, _ := json.Marshal(&g) return string(data) } func main() { g := &Girl{Name: "yueyue"} fmt.Printf("g0: %p\n", g) g.SetColor("white") fmt.Println(g.GetJson()) }
輸出結果:
g0: 0xc42000a060g1: 0xc42000a060
{"name":"yueyue","dress_color":"white"}
能夠發現,函數傳指針先後是對象的內存地址是相同的,因此 SetColor
將會生效。咱們還能夠看下實際上傳遞的依然是值拷貝,只不過是指針拷貝了一份副本,兩個指針指向相同的 g
對象而已,代碼以下:
package main import ( "encoding/json" "fmt" ) type Girl struct { Name string `json:"name"` DressColor string `json:"dress_color"` } func (g *Girl) SetColor(color string) { fmt.Printf("g1: %p\n", &g) // 取指針的地址 g.DressColor = color } func (g *Girl) GetJson() string { data, _ := json.Marshal(&g) return string(data) } func main() { g := &Girl{Name: "yueyue"} fmt.Printf("g0: %p\n", &g) // 取指針的地址 g.SetColor("white") fmt.Println(g.GetJson()) }
輸出結果:
g0: 0xc42000c028g1: 0xc42000c038
{"name":"yueyue","dress_color":"white"}
因此,在 Golang
中全部函數參數傳遞都是值拷貝,傳指針只是拷貝了一份指針副本,同時指向原對象。
小結:在函數傳參過程當中,須要合理使用傳值、傳指針。通常狀況下,須要改變原始對象值、傳遞大的結構體,傳指針是最合適的,由於傳一個內存地址的開銷很小。反之,若是變量不可變動、map
或 slice
應該選擇傳值方式。