最近在使用golang append的时候,发现了一个坑,特意记录一下,下面是例子:
var a = []int32{1, 2, 3, 4, 5}
var b = append(a[:1], a[2:]...)
fmt.Println(a) // [1 3 4 5 5]
fmt.Println(b) // [1 3 4 5]
想当然的觉得操作b进行append不会影响到a,但是结果却影响到了,于是我又特意看了下append的源码:
// The append built-in function appends elements to the end of a slice. If
// it has sufficient capacity, the destination is resliced to accommodate the
// new elements. If it does not, a new underlying array will be allocated.
// Append returns the updated slice. It is therefore necessary to store the
// result of append, often in the variable holding the slice itself:
//
// slice = append(slice, elem1, elem2)
// slice = append(slice, anotherSlice...)
//
// As a special case, it is legal to append a string to a byte slice, like this:
//
// slice = append([]byte("hello "), "world"...)
func append(slice []Type, elems ...Type) []Type {
m := len(slice)
n := m + len(elems)
if n > cap(slice) {
// 扩容逻辑
// Grow slice
newcap := cap(slice)
doublecap := newcap + newcap
if doublecap < n {
newcap = n
} else {
if old := len(slice); old < 1024 {
newcap = doublecap
} else {
// Check 0 < newcap to detect overflow
// and prevent an infinite loop.
for 0 < newcap && newcap < n {
newcap += newcap / 4
}
// Set newcap to the new capacity, doubling
// it if that's what occurred.
if newcap <= 0 {
newcap = n
}
}
}
newslice := make([]Type, n, newcap)
copy(newslice, slice)
slice = newslice
}
slice = slice[0:n]
copy(slice[m:n], elems)
return slice
}
总的来说,就是判断对象是否append之后的长度是否超过它本身的cap,超过就扩容,不超过就是用之前的切片,并且新加的元素会覆盖之前的元素,也就是这个
slice = slice[0:n]
copy(slice[m:n], elems)
所以才会导致a的值发生了变化,为此我对a和b的切片地址和数组地址进行了打印,结果符合预期
var a = []int32{1, 2, 3, 4, 5}
var b = append(a[:1], a[2:]...)
fmt.Printf("切片地址:%p,长度:%v,容量:%v,添加元素长度:%v\n", &a, len(a[:1]), cap(a[:1]), len(a[2:]))
fmt.Printf("切片地址:%p\n", &b)
// 获取切片底层数组的地址
fmt.Printf("数组地址:%p\n", &a[0])
fmt.Printf("数组地址:%p\n", &b[0])
// 终端打印
切片地址:0xc000008db0,长度:1,容量:5,添加元素长度:3
切片地址:0xc000008dc8
数组地址:0xc00001e738
数组地址:0xc00001e738