【Go - MD太可怕了,小心! Go中Slice的append陷阱】

开始之前,先看下下面这个例子,

package main

import "fmt"

func main() {
	s := make([]int, 0, 10)

	s1 := append(s, 1)
	s1 = append(s1, 3)

	s2 := append(s, 2)
	s2 = append(s2, 4)

	fmt.Printf("%v\\n", s1)
	fmt.Printf("%v\\n", s2)
}

这个输出什么 , [1,3] [2,4]?

错❌,输出的是[2,4] , [2, 4]。

为什么

  1. 先来看下slice,
type slice struct {
	array unsafe.Pointer
	len   int
	cap   int
}

可以看出 Slice 数据结构是 指针 ,容量(可以存储数据的大小),长度(当前数据的大小)。使用上 ,make可以为其分配空间。

make([]int, 0, 10) 
  1. 这里有个注意点,append操作,append(slice, …),
  • 如果slice开辟的空间足够,则不用额外分配空间。 append的返回结果的空间仍然是slice原有分配的空间,只是基于这个空间赋值,然后返回
  • 如果slice开辟的空间不足,则重新开辟空间,赋值,然后返回。
  1. 再带入这个例子,
	s := make([]int, 0, 10)

	s1 := append(s, 1)
	s1 = append(s1, 3)

	s2 := append(s, 2)
	s2 = append(s2, 4)

站在第二个slice的角度s2上看,先拿到一块内存空间,然后从这个空间的第一个,第二个位置分别写入值。则此时会把s1之前写入的覆盖掉。

结语

  • append追加元素,如果slice还有容量的话,则会继续使用原先开票的空间。不会重新开辟空间。
  • 当底层数组装不下的时候,Go就会创建新的底层数组来保存这个切片,slice地址也随之改变。

append是否返回新地址,和原有容量和添加数据数量有关,不是固定的。所以一定不要默认append会进行新的内存开辟,更不要拿append 作为复制操作,认为append完以后拿到的slice一定就是全新独立的slice。
所以在使用append的时候,一定小心谨慎,认真看好空间的分配,防止相同地址被覆盖写入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值