slice注意事项

slice

slice本身并不是动态数组和数组指针,而是一个结构,通过设置内部指针和其他属性来访问一部分数据

slice底层结构

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

  • array为指针,指向底层数组
  • len 内部元素个数
  • cap 容量
  • 数量关系为 0 <= len <= cap

slice创建方式

  • array := []int{1, 2, 3, 4, 5, 6, 7}
    slice := array[low:high] 区间左闭右开
  • slice := []int{1, 2, 3}
  • slice := make([]int, len, cap)

测试示例

func main() {
	slice1 := []int{1, 2, 3, 4, 5}
	slice2 := slice1[:8]
	_ = slice2
	fmt.Printf("slice1.array %p ,slice2.array, %p\n  slice2 : %v", &slice1[0], &slice2[0], slice2)
}

测试结果

panic: runtime error: slice bounds out of range [:8] with capacity 5
goroutine 1 [running]:
main.main()
/home/shenglish/go-workspace/testdir/main.go:59 +0x5b

结果分析

运行时错误说明某个底层数组的slice的cap无法超过底层数组的cap[如果你发现一个slice cap超过底层数组的大小那么此时slice指向的数组一定已经不是原先的数组了]

append

slice的扩容和元素删除都通过append函数。提出个问题,对一个slice扩容后,再修改slice中的元素,是否会影响底层数组?

测试代码

func main() {
	slice1 := []int{1, 2, 3, 4, 5, 6, 7}
	slice2 := slice1[:5]
	fmt.Printf("%p, %v\n", slice1, cap(slice2))
	fmt.Printf("slice1.array %p ,slice2.array, %p slice1.array %v, slice2 : %v\n", &slice1[0], &slice2[0], slice1, slice2)
	slice2[2] = 10000
	fmt.Printf("slice1.array %p ,slice2.array, %p slice1.array %v, slice2 : %v\n", &slice1[0], &slice2[0], slice1, slice2)
	slice2 = append(slice2, 123)
	fmt.Printf("slice1.array %p ,slice2.array, %p slice1.array %v, slice2 : %v\n", &slice1[0], &slice2[0], slice1, slice2)
	slice2 = append(slice2, 10, 20, 30, 40, 50)
	fmt.Printf("slice1.array %p ,slice2.array, %p slice1.array %v, slice2 : %v\n", &slice1[0], &slice2[0], slice1, slice2)
	slice2[2] = 11111
	fmt.Printf("slice1.array %p ,slice2.array, %p slice1.array %v, slice2 : %v\n", &slice1[0], &slice2[0], slice1, slice2)
}

测试结果

0xc000020080, 7
slice1.array 0xc000020080 ,slice2.array, 0xc000020080 slice1.array [1 2 3 4 5 6 7], slice2 : [1 2 3 4 5]
slice1.array 0xc000020080 ,slice2.array, 0xc000020080 slice1.array [1 2 10000 4 5 6 7], slice2 : [1 2 10000 4 5]
slice1.array 0xc000020080 ,slice2.array, 0xc000020080 slice1.array [1 2 10000 4 5 123 7], slice2 : [1 2 10000 4 5 123]
slice1.array 0xc000020080 ,slice2.array, 0xc00001e0e0 slice1.array [1 2 10000 4 5 123 7], slice2 : [1 2 10000 4 5 123 10 20 30 40 50]
slice1.array 0xc000020080 ,slice2.array, 0xc00001e0e0 slice1.array [1 2 10000 4 5 123 7], slice2 : [1 2 11111 4 5 123 10 20 30 40 50]

结果分析:

  • 对比第一行和第二行输出,可以验证&slice1[0]就是底层数组的地址,且slice2的cap默认为底层数组slice的cap大小,len为5
  • 对比2,3行输出,验证对slice修改,可以直接修改底层数组元素
    使用append扩容
  • 对比3,4行输出,发现一个奇怪的现象,123添加到了slice2后面,但没有添加到slice1末尾,反而占据了6的位置。对len为5,cap为7的slice2append时,实际上就是对底层数组从slice2最后一个下标+1映射的底层数组位置进行覆盖[不好描述哈哈哈]
  • 对比4,5 行发现,这时,底层数组却没有变化。仅slice2添加了10,20,30,40,50。并且slice2底层数组的地址发生了变化

下面是个人参考书籍实现的append函数,帮助上面现象的理解

append 个人实现

func appendInt(dist []int, src ...int) []int {
	var tmp []int
	srclen := len(src)
	distlen := len(dist)
	dataLen := srclen + distlen
	if srclen+distlen <= cap(dist) {
		tmp = dist[:]
	} else {
		tmp = make([]int, dataLen, 2*dataLen)
		copy(tmp, dist)
	}
	// tmp[distlen:] = src[:]
	copy(tmp[distlen:], src[:srclen])
	return tmp
}

append实现删除

slice删除某个元素

slice1 = slice1[:index]+slice[index+1:]

备注

slice 之间按值拷贝,拷贝不影响内部指针指向

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值