golang中的slice很灵活,功能也很强悍,不过对于初学者来说会容易被它坑到,此篇文章就尽量提及到使用slice的一些容易容易出错的地方,以下示例使用的golang版本为1.14.2。
作为参数传递
在go语言中的方法的参数都为值传递,切片也不例外,但是切片有一个指向数据地址的指针,所以就算是值传递,如果改变切片中的已有元素数据,也有可能影响到原有的切片
为什么是可能呢?我们来看下面的代码
func test(a []int) {
a = append(a, 1)
a[0] = 2
}
func main() {
s1 := make([]int, 1, 1)
log.Printf("%v\n", s1) // [0]
test(s1)
log.Printf("%v\n", s1) // [0]
}
这里的s1变量并没有被影响到数据;那么我们再来看下面的代码
func test(a []int) {
a = append(a, 1)
a[0] = 2
}
func main() {
s1 := make([]int, 1, 2)
log.Printf("%v\n", s1) // [0]
test(s1)
log.Printf("%v\n", s1) // [2]
}
咦,这里的s1就已经被改变了,这是为什么呢?我们可以看看append方法的注解,如果目的切片容量足够,那么会直接添加到原切片指向的数据地址再返回一个拥有新长度和原数据地址的新切片,所以直接修改原切片长度内的数据会影响到原切片数据;目的切片容量不足时才会另外申请空间,此时新切片拥有新的数据地址,所以不会影响到原有切片。
注意append总是会返回一个新切片;原切片容量足够时,新切片指向的数据地址不变,依然是原切片指向的数据地址
作为方法返回值
当切片作为方法返回值时也有几点值得注意
只返回了一个切片的一部分时,并不会复制数据,而是直接指向原地址
这样的话如果一直在引用返回的切片时,整个切片都不会被释放,一直在内存中
如果返回的切片是一个很多地方都可以获取到的同一切片变量,比如
var a = []int{1, 2, 1}
func GetA() []int{
return a
}
那么此时也没有真正做到值传递,外面的修改可能会影响到a,并且如果有多个协程获取并修改的话,还有并发同步问题
我们可以以下面的方式返回一个指向新地址的slice解决上面的问题,做到真正的值传递
func GetA() []int{
return append([]int{}, a...)
}
nil切片
一个nil切片也是可以使用len()方法的,此时为0,如果不需要判断切片是否为nil则直接使用len()方法即可
有疑问加站长微信联系(非本文作者)