This page looks best with JavaScript enabled

Go 的 MAP 要不要用指標

 ·  ☕ 2 min read

一開始用 Go 的 Map 時常常搞不清楚到底要不要傳指標,好像大部分的時候都不用,但用到 unmarshal 之類的 function 就又要傳指標進去了。

這篇就來研究一下 Go 的 Map 到底是什麼生物

先看看下面這段程式碼

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// mv = map value
func ModifyMap(mv map[int]int) {
	mv[1] = 1
}

// mp = map pointer
func ModifyMapByPointer(mp *map[int]int) {
	(*mp)[1] = 1
}

func main() {
	m1 := make(map[int]int)
	ModifyMap(m1)
	fmt.Println(m1) // map[1:1]
    
	m2 := make(map[int]int)
	ModifyMapByPointer(&m2)
	fmt.Println(m2) // map[1:1]
}

兩個 function 都改到 map 裡面的值了,所以用不用指標到底有沒有差呢?

再看看下面這段程式碼

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
func MakeMap(mv map[int]int) {
	mv = make(map[int]int)
	mv[1] = 1
}

func MakeMapByPointer(mp *map[int]int) {
	(*mp) = make(map[int]int)
	(*mp)[1] = 1
}

func main() {
	var m1 map[int]int
	MakeMap(m1)
	fmt.Println(m1) // map[]

	var m2 map[int]int
	MakeMapByPointer(&m2)
	fmt.Println(m2) // map[1:1]
}

這次的 MakeMap 的操作就沒有反應到 main 傳進去的 m1 上,這是怎麼回事呢?

首先當我們使用 make 產生 map 時,它就是回傳一個 pointer 給我們。也就是說當我們呼叫了 m := make(map[int]int),m 這個變數內存的其實是一個地址,指到真正的 map struct。

來看看第一組程式碼發生了什麼事
簡單來說就是 m1 把 map 的地址 x 傳進去 ModifyMap,mv 內就存著相同的值 x,所以可以改到真正的 map struct。

1
2
3
4
5
6
7
func ModifyMap(mv map[int]int) {
	mv[1] = 1
}

m1 := make(map[int]int)
ModifyMap(m1)
fmt.Println(m1) // map[1:1]

yo

下面這部分,是把 m2 本身的位置 y 傳進 ModifyMapByPointer, mp 就存著 m2 的位置 y。
接著透過指標改到了 map struct

1
2
3
4
5
6
7
func ModifyMapByPointer(mp *map[int]int) {
	(*mp)[1] = 1
}

m2 := make(map[int]int)
ModifyMapByPointer(&m2)
fmt.Println(m2) // map[1:1]

yo

再來看看在 function 裡面 make map 會怎麼樣

這裡 m1 一開始是 nil,傳進 MakeMap 的東西也是 nil,自然 mv 內也會存著 nil。
接著呼叫了 make,mv = make(map[int]int)把真正 map struct 的地址存到了 mv 內,mv[1] = 1也是真的有改到 map 裡面的值,只是外面的 m1 內存的值還是 nil,所以我們 print 出來的也就是空 map 了。

1
2
3
4
5
6
7
8
func MakeMap(mv map[int]int) {
	mv = make(map[int]int)
	mv[1] = 1
}

var m1 map[int]int
MakeMap(m1)
fmt.Println(m1) // map[]

yo

最後一部分的範例,m2 的地址 y 傳進了 MakeMapByPointer,mp 內存著的是 m2 的地址,當呼叫(*mp) = make(map[int]int)時所代表的是產生一個 map struct,並把該地址 x 存進 mp 所指到的變數內,也就是 m2 的值中,所以後續的更改我們也可以從 m2 看到拉

1
2
3
4
5
6
7
8
func MakeMapByPointer(mp *map[int]int) {
	(*mp) = make(map[int]int)
	(*mp)[1] = 1
}

var m2 map[int]int
MakeMapByPointer(&m2)
fmt.Println(m2) // map[1:1]

yo

至於真正的 map struct 是一個叫做 hmap 的東西,這邊就不講那麼細,剩下的上網估狗 Go makemap hmap 之類的關鍵字都可以找到嚕

Share on

Marko Peng
WRITTEN BY
Marko Peng
Good man