背景
關于slice和map是指傳遞還是引用傳遞,很多文章都分析得模棱兩可,其實在Go中只有值傳遞,但是很多情況下是因為分不清slice和map的底層實現,所以導致很多人在這一塊產生疑惑,下面通過代碼案例分析slice和map到底是值傳遞還是引用傳遞。
案例分析
func main() {list := make([]int, 10)fmt.Printf("list addr:%p\n", list)fmt.Println("list size:", len(list))listExpand(list)fmt.Println("expand list size:", len(list))m := make(map[int]int, 0)fmt.Printf("map addr:%p\n", m)fmt.Println("map size:", len(m))mapExpand(m)fmt.Println("expand size:", len(m))
}func mapExpand(m map[int]int) {for i := range 10 {m[i] = i}fmt.Printf("expand map addr:%p\n", m)
}func listExpand(list []int) {for i := range 10 {list = append(list, i)}fmt.Printf("expand list addr:%p\n", list)
}
上面代碼的輸出結果:
list addr:0xc000010500
list size: 10
expand list addr:0xc0000220a0
expand list size: 10
map addr:0xc00001e180
map size: 0
expand map addr:0xc00001e180
expand size: 10
可以清楚的看到,都沒有使用指針的情況下,兩者的結果都不一樣,map在擴容前后都是同一個內存地址,但是slice在沒擴容都不為同一個內存地址。
可能會更疑惑了,這結果是想說明,map是引用傳遞,slice是值傳遞嗎?
在Go的1.8版本源碼中
hashmap的make實現如下:
可以清楚的看到,通過make創建的map其實返回的是hamp結構體的指針。
slice的make實現如下:
可以清楚的看到,通過make創建的slice其實返回的是slice的結構體,并不是指針。
所以,結論很清晰了,其實Go中只有值傳遞,因為各個引用類型底層實現的不同導致的結果不一致,因為創建map返回的是指針,所以傳遞map時,擴容了也不會導致和預期不一樣的結果;而slice的創建返回的是結構體,如果擴容了,則會導致底層數組的變化,不一定是預期的結果。