开始看golang的时候,最纠结的就是函数传递slice,这个slice到底有没有发生变化。最近发现周围的同事也没有弄清楚,于是打算仔细看下,在函数里修改传递的slice会不会改变原来的slice。
slice 结构
首先从slice的结构入手,可以看到slice底层其实就是一个数组
1 2 3 4 5 6 7 8 9 10 11
| type slice struct { array unsafe.Pointer len int cap int }
type SliceHeader struct { Data uintptr Len int Cap int }
|
函数传递
需要明确的一点是,golang是值传递,copy value
- 传值
例子1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package main import "fmt"
func modify(B []int) { for i := range s { B[i]++: } }
func main() { A := []int{1, 2, 3} modify(A) fmt.Println(A) }
|
那么s究竟会不会改变呢?答案是,会改变的。问题来了,不是说golang函数传递是传值么?这时候不应该copy一份slice么?那为什么main函数里的slice会发生变化呢?
打印出来的结果是
接下里利用unsafe包直接对比下main函数和modify函数的slice对应的底层数组是不是一个,如果底层数组是一个,那显然在modify函数修改会影响main函数的slice。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package main import ( "fmt" "reflect" "unsafe" )
func modify(B []int) { fmt.Println((*reflect.SliceHeader)(unsafe.Pointer(&B)).Data) for i := range B { B[i]++ } }
func main() { A := []int{1, 2, 3} fmt.Println((*reflect.SliceHeader)(unsafe.Pointer(&A)).Data) modify(A) fmt.Println(A) }
|
打印出来的结果是
1 2 3
| 824634466336 824634466336 [2 3 4]
|
打印之后发现,指向的是同一个数组呀,那就验证了我们的猜测。其实传值的时候,相当于是这个样子的
main: A –> SliceHeader
Modify: B –> 同一个SliceHeader
例子2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package main
import "fmt"
func appendToS(s []int) []int { s = append(s, 100) return s }
func main() { s := []int{1, 2, 3} newS := appendToS(s) fmt.Println(s) fmt.Println(newS) }
|
看了例子1,你很开心的说,打印出来的s和newS是一样的,但我要告诉你猜错辽。你不由得感叹这是为啥子啊?明明指向一个数组,为什么没有变化呢?
打印结果如下
再利用unsafe包搞一把
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package main
import ( "fmt" "reflect" "unsafe" )
func appendToS(s []int) []int { s = append(s, 100) return s }
func main() { s := []int{1, 2, 3}
sptr := (*reflect.SliceHeader)(unsafe.Pointer(&s)) fmt.Println(sptr.Len)
newS := appendToS(s) fmt.Println(s) fmt.Println(newS)
fmt.Println(sptr.Len) fmt.Println(sptr.Cap) fmt.Println((*reflect.SliceHeader)(unsafe.Pointer(&newS)).Len) fmt.Println((*reflect.SliceHeader)(unsafe.Pointer(&newS)).Cap) }
|
打印结果是
1 2 3 4 5 6 7
| 3 [1 2 3] [1 2 3 100] 3 3 4 6
|
这个是因为原来的数组容量是3,append发现容量不够,触发了扩容,这时候底层数组就变掉了,如果容量充足的话,调大SliceHeader的长度是可以看到新增的元素。参考下runtime包slice.go的growslice函数。
- 传指针 无需多言,肯定会变化