Go 在函数中引用的 slice踩的坑。

25 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情

作为一名 Go 的初学者,在写demo时候经常遇到一些奇奇怪怪的问题,今天在用Go写slice 的demo的时候遇到了一个很奇怪的问题,因为想着slice 是引用,类型就在函数中给slice添加元素,但是一直发现无论怎么添加,就是添加不进去。来记录下这个问题以及解决方式

修改内容


package main

import "fmt"

func main() {
   list := []int{1,2,3}
   fmt.Printf("%v\n",list)
   change(list)
   fmt.Printf("%v\n",list)
}

func change(list []int)  {
   list[0] = 111
}
➜  go_server git:(vue) ✗ go run dd.go
[1 2 3]
[111 2 3]

我们发现直接修改slice里的内容是没有问题的。

添加元素

ackage main

import "fmt"

func main() {
   list := []int{1,2,3}
   fmt.Printf("pre add :%v\n",list)
   add(list)
   fmt.Printf("after add :%v\n",list)
}

func add(list []int) {
   list = append(list, 999)
}
➜  go_server git:(vue) ✗ go run dd.go
pre add :[1 2 3]
after add :[1 2 3]

出问题了 ,这是怎么回事,我明明插入了一条数据啊!

slice 不是引用类型吗?

我们来分析一下 slice 的结构

type slice struct {
	array unsafe.Pointer
	len   int
	cap   int
}

问题复现

其实 sclice 的结构 是一个指向 array 的指针,还有slice的长度和容量。

所以我们传入slice 的类型的时候,其实传入的是这个结构体

image.png

这样我们就看的很清楚了

问题分析

需要注意几点儿内容:

  • slcie 在函数之间调用的时候传入的是slice结构体(是值拷贝)
  • 在函数中修改slice 内容时候,并不会引起 slice 指向数组地址的变化,所以修改时候也就是直接改的底层数组的数值,表面上看 就是直接修改了函数外边儿slice 的数值
    • 如果在函数体内append 了比较多的元素,发生了扩容,也只是改变了 函数参数中值拷贝的slice 的array 地址指向,外围的slice 的 底层数组 指针是没有修改的,所以就出现了,apped 数据,在外边儿 失效的假象。
    • 如果在函数体内apped 比较少的元素,没有发生扩容,这时候,函数内部 的slice 结构体的len发生了改变,但是因为传入时候是值拷贝,len是int型,不能改变main函数中slice 的len,但是len是决定slice 指向数组可见范围的一个变量。也会导致,apped 数据,在外边儿 失效的假象。

问题解决

只需要我们在传递slice的时候使用指针类型就可以了。

image.png