开始之前,先看下下面这个例子,
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]。
为什么
- 先来看下slice,
type slice struct {
array unsafe.Pointer
len int
cap int
}
可以看出 Slice 数据结构是 指针 ,容量(可以存储数据的大小),长度(当前数据的大小)。使用上 ,make可以为其分配空间。
make([]int, 0, 10)
- 这里有个注意点,append操作,append(slice, …),
- 如果slice开辟的空间足够,则不用额外分配空间。 append的返回结果的空间仍然是slice原有分配的空间,只是基于这个空间赋值,然后返回
- 如果slice开辟的空间不足,则重新开辟空间,赋值,然后返回。
- 再带入这个例子,
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的时候,一定小心谨慎,认真看好空间的分配,防止相同地址被覆盖写入。