在 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
咦,爲啥顏色沒有設置成功?orm
仔細思考,原來就是今天要分析的 Golang
中關於函數傳值與傳指針的區別沒搞清楚。首先,咱們看到 SetColor
和 GetJson
函數都是值傳遞,因此實際在 main
中調用 g.SetColor
的時候,是拷貝了一份副本給函數 SetColor
,而後在函數內對副本進行了 color
的設置;但實際上此時原來的 g
對象卻依然只有 name
屬性,因此輸出了以上結果。cdn
咱們來打印一下傳值先後對應的 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: 0xc42000a060
g1: 0xc42000a080
{"name":"yueyue","dress_color":""}string
發現確實 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: 0xc42000a060
g1: 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: 0xc42000c028
g1: 0xc42000c038
{"name":"yueyue","dress_color":"white"}
因此,在 Golang
中全部函數參數傳遞都是值拷貝,傳指針只是拷貝了一份指針副本,同時指向原對象。
小結:在函數傳參過程當中,須要合理使用傳值、傳指針。通常狀況下,須要改變原始對象值、傳遞大的結構體,傳指針是最合適的,由於傳一個內存地址的開銷很小。反之,若是變量不可變動、map
或 slice
應該選擇傳值方式。