golang中的slice函数传参到底是值还是指针

疑问

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

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

10 + 14 =