golang面試基礎系列-傳值&傳指針(二)

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 中關於函數傳值與傳指針的區別沒搞清楚。首先,咱們看到 SetColorGetJson 函數都是值傳遞,因此實際在 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: 0xc42000a060

g1: 0xc42000a080string

{"name":"yueyue","dress_color":""}it

發現確實 g0g1 對象的內存地址是不一樣的,說明是兩個不一樣的對象。接下來,咱們看一下傳指針的狀況:

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 中全部函數參數傳遞都是值拷貝,傳指針只是拷貝了一份指針副本,同時指向原對象。

小結:在函數傳參過程當中,須要合理使用傳值、傳指針。通常狀況下,須要改變原始對象值、傳遞大的結構體,傳指針是最合適的,由於傳一個內存地址的開銷很小。反之,若是變量不可變動、mapslice 應該選擇傳值方式。

稻草人生

相關文章
相關標籤/搜索