golang 使用下标复制切片的踩坑记录

最近排查了一个问题,最后发现是由于对golang的切片实现不熟悉导致踩了坑。

切片的底层结构由数组指针,切片容量cap,切片当前长度len组成。

一、切片的三种初始化方式的区别

  1. 字面值

会根据传入的字面值初始化底层数组并创建切片

例子

arr := []int{1, 2, 3}
  1. 下标初始化

会创建一个指向原数组的切片结构体,不会拷贝原数组或者原切片中的数据,所以修改新切片的数据也会修改原切片。

例子

arr := [3]int{1, 2, 3}
slice := arr[0:1]
  1. make关键字初始化

会根据传入的len和cap创建底层数组并创建切片

arr := make([]int, 10)

二、切片复制

  1. 使用下标,从旧切片生成新切片

例子

arr := []int{1, 2, 3, 4}
arr2 := arr[0:2]   // 1, 2

此时,两个切片共用底层数组结构,对arr2中的值进行修改会造成对arr的修改。事实上,只要是对底层公共数组的修改,都会作用到切片上。

func TestSlice(t *testing.T)  {
	arr1 := []int{1, 2, 3, 4}
	arr2 := arr1[:2]

	fmt.Println(arr1)  //[1 2 3 4]
	fmt.Println(arr2)  //[1 2]

	arr2[0] = 5

	fmt.Println(arr1)	//[5 2 3 4]
	fmt.Println(arr2)	//[5 2]

	arr2 = append(arr2, 6)

	fmt.Println(arr1)	//[5 2 6 4]
	fmt.Println(arr2)	//[5 2 6]
}

插入数据时,如果底层数组容量足够则会修改原来的数组;否则会引起扩容,从而新建一个底层数组。

容量充足

func TestSlice(t *testing.T)  {
	arr1 := make([]int, 3, 4)	//len 3  cap 4
	arr1[0] = 1
	arr1[1] = 2
	arr1[2] = 3

	arr2 := arr1[2:]	//len 1 cap 2

	fmt.Println(arr1)  //[1 2 3]
	fmt.Println(arr2)  //[3]

	arr2 = append(arr2, 6)	//arr2容量足够,仍然修改原来的数组

	fmt.Println(arr1)	//[1 2 3]
	fmt.Println(arr2)	//[3 6]

	arr1 = append(arr1, 7)
	fmt.Println(arr1)	//[1 2 3 7]
	fmt.Println(arr2)	//[3 7]
}

容量不足

func TestSlice(t *testing.T)  {
	arr1 := []int{1, 2, 3, 4}	//len 4  cap 4
	arr2 := arr1[2:]			//len 2 cap 2

	fmt.Println(arr1)  //[1 2 3 4]
	fmt.Println(arr2)  //[3 4]

	arr2 = append(arr2, 6)	//arr2 扩容生成新数组,底层不再共用
	arr2[0] = 5

	fmt.Println(arr1)	//[1 2 3 4]
	fmt.Println(arr2)	//[5 4 6]
}

总结,使用下标复制分片的方式会使用共同的底层数组,如果需要对分片进行修改,有可能对原始数据造成修改。

  1. copy拷贝

指定原切片和目标切片的大小,copy会将内存中的数据进行复制,属于深度拷贝。

func TestSlice(t *testing.T)  {
	arr1 := []int{1, 2, 3, 4}
	arr2 := []int{5, 6}

	fmt.Println(arr1)  //[1 2 3 4]
	fmt.Println(arr2)  //[5 6]

	copy(arr2, arr1)

	fmt.Println(arr1)  //[1 2 3 4]
	fmt.Println(arr2)  //[1 2]

	arr2[0] = 5

	fmt.Println(arr1)  //[1 2 3 4]
	fmt.Println(arr2)  //[5 2]
}

参考资料

https://draveness.me/golang/docs/part2-foundation/ch03-datastructure/golang-array-and-slice/#32-%E5%88%87%E7%89%87

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值