持续创作,加速成长!这是我参与「掘金日新计划 · 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 的类型的时候,其实传入的是这个结构体
这样我们就看的很清楚了
问题分析
需要注意几点儿内容:
- slcie 在函数之间调用的时候传入的是slice结构体(是值拷贝)
- 在函数中修改slice 内容时候,并不会引起 slice 指向数组地址的变化,所以修改时候也就是直接改的底层数组的数值,表面上看 就是直接修改了函数外边儿slice 的数值
- 如果在函数体内append 了比较多的元素,发生了扩容,也只是改变了 函数参数中值拷贝的slice 的array 地址指向,外围的slice 的 底层数组 指针是没有修改的,所以就出现了,apped 数据,在外边儿 失效的假象。
- 如果在函数体内apped 比较少的元素,没有发生扩容,这时候,函数内部 的slice 结构体的len发生了改变,但是因为传入时候是值拷贝,len是int型,不能改变main函数中slice 的len,但是len是决定slice 指向数组可见范围的一个变量。也会导致,apped 数据,在外边儿 失效的假象。
问题解决
只需要我们在传递slice的时候使用指针类型就可以了。