疑问
由于经常在网上看文章,看到有说明golang的slice是默认传递指针类型,在使用函数传参的时候也是使用指针,所以本文用具体的代码输出来说明这个疑问。
代码示例
// go version go1.13.4 darwin/amd64 func main(){ s := []int{1,2,3} sliceTestAppend(s) // append调用 fmt.Println(s) sliceTestUpdate(s) // 更新slice中的内容 fmt.Println(s) slicePointerTestAppend(&s) // 传递slice指针append调用 fmt.Println(s) } func sliceTestUpdate(s []int){ for k,v := range s{ s[k] = v+1 } } func sliceTestAppend(s []int){ a := []int{4,5,6} s = append(s,a...) } func slicePointerTestAppend(s *[]int){ a := []int{4,5,6} *s = append(*s,a...) }
输出内容:
[1 2 3] [2 3 4] [2 3 4 4 5 6]
结果说明
由上图代码以及代码输出可以得出以下结论:
- 传递值,当存在slice的长度变化,结果未改变
- 传递值,当slice中的值修改,结果生效
- 当传递slice指针的时候,长度变化以及值修改都会生效
为什么会这样?
golang在函数传参数的时候,本身都是值拷贝,而slice本身的数据类型为sliceHeader,结构体如下:
type sliceHeader struct { Data unsafe.Pointer Len int Cap int }
由上面结构体,可以看出本身的数据Data存在底层数组当中,本身是一个指针类型,结构体存在Len、Cap属性。
当函数传参的时候,本身是sliceHeader的值拷贝,当存在长度变化的时候,Len与Cap并没有生效,存在值更改的时候,由于Data是指针,所以slice的值修改是生效的,当函数传参为slice指针的时候,sliceHeader本身是个指针,存在属性改动的时候直接生效,所以会得出上述的三个结论。