疑问
由于经常在网上看文章,看到有说明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本身是个指针,存在属性改动的时候直接生效,所以会得出上述的三个结论。