切片传参中的坑

Finn · · 848 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

一,切片传参

切片在工作中使用频率很高,但是不谨慎使用的话,很容易踩坑,特别是在传参的时候。话不多说,先看一段代码:

func main() {
	slice := make([]int, 1, 3)
	fmt.Printf("before,slice %v, addr is %p \n", slice, &slice)
 	changeSlice(slice)
 	fmt.Printf("after,slice %v, addr is %p \n", slice, &slice)
}

func changeSlice(slice2 []int) {
	slice2 = append(slice2, 5)
	slice2[0] = 1
	fmt.Printf("during,slice2 %v, addr is %p \n", slice2, &slice2)
}

想想输出结果是什么?

before,slice [0], addr is 0xc000084020 
during,slice2 [1 5], addr is 0xc000084060 
after,slice [1], addr is 0xc000084020

从输出的第二段可以知道,在changeSlice方法中的切片地址和main方法中的切片地址不一样,所以可以看出,切片在作为参数进行传递的时候,形参对实参进行了拷贝,

是值传递而不是引用传递,然后观察第三行输出,执行changeSlice方法后slice被改变了,第一个元素从0变成了1,这是为哈?因为切片中包含三个参数,分别是:

  1. 长度:表示当前包含的元素总数。
  2. 容量:即配置的内存位置总数。
  3. 指向数据序列的指针

slice和slice2的指针是指向同一个数组的,(如果不是很清楚go中数组和切片的关系,可以看下这位同学的文章https://studygolang.com/articles/21543#reply0),changeSlice方法改变了它们指向的底层数组的第一个元素,

所以after中的slice的第一个元素也被改变了。

二,append方法中的坑

清楚了原因之后,我们对上述的代码进行一些改动,再多加两个元素:

func main() {
	slice := make([]int, 1, 3)
	fmt.Printf("before,slice %v, addr is %p \n", slice, &slice)
 	changeSlice(slice)
 	fmt.Printf("after,slice %v, addr is %p \n", slice, &slice)
}

func changeSlice(slice2 []int) {
	slice2 = append(slice2, 5, 6, 7)
	slice2[0] = 1
	fmt.Printf("during,slice2 %v, addr is %p \n", slice2, &slice2)
}

再思考一下输出会是怎么样的?

before,slice [0], addr is 0xc000084020 
during,slice2 [1 5 6 7], addr is 0xc000084060 
after,slice [0], addr is 0xc000084020

好像和第一次输出没有差别,但仔细看下第三行输出,会发现和之前的输出不一样的,slice第一个元素并没有被改变,这又是为哈?

这里就是append方法的坑了

切片在创建时,底层匿名数组也随之被创建,数组长度等于切片的容量,当使用append方法追加元素的时候,如果追加的元素数量小于切片的剩余容量,那么就直接在切片尾部进行追加,

一点毛病没有,但是如果超出了切片的剩余容量,那么就要对底层的数组进行拷贝和扩容了。

此时append的原理大致如下:

1,新建一个切片s,s的长度和原切片一样,但是容量是原来的两倍,新建切片的时候自然会新建底层数组。

2,将原数组的元素拷贝到新的数组,也就是把原切片中的元素拷贝到s中。

3,把s赋值给原切片,然后进行小于剩余容量的append操作。

所以,这下我们就明白为什么以上输出的第三行slice的第一个元素没有被改变了,因为slice2指向的底层数组和slice的不一样了。

三,结论

从上面我们可以看出,切片传参使用不当的话,可能真的会踩坑,所以涉及到切片追加的场景,最好还是将改变后的切片return,防止出现数据丢失的情况。

 

 

go语言冯小白,若上述有错误之处,还请指出。

 


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

848 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传