切片带来了很多问题,考虑到性能原因,将一个切片进行切片,不会复制底层的数组。这意味着切片的子切片只是遵循原始切片变化的视图。
因此,如果想要将它与初始的切片分开请不要忘记 使用copy(from_slice, to_slice)。
对于 append 函数,忘记 copy() 会变得更加危险:
如果原始切片没有足够的容量来保存新值,底层数组将会重新分配内存和大小。这意味着 append 的结果能不能指向原始数组取决于它的初始容量。这会导致难以发现的不确定 bugs。
在下面的代码中,我们看到为子切片追加值的影响取决于原始切片的容量:
import "fmt"
// 切片是引用传递
func doStuff(value []string) {
fmt.Printf("value=%v\n", value)
value2 := value[:]
value2 = append(value2, "b", "c", "d")
fmt.Printf("value=%v, value2=%v\n", value, value2)
value2[0] = "1"
value2[1] = "2"
fmt.Printf("value=%v, value2=%v\n", value, value2)
}
func main() {
slice1 := []string{
"a"} // 长度 1, 容量 1
doStuff(slice1)
// Output:
// value=[a] -- ok
// value=[a], value2=[a b c d] -- ok: value 未改变, value2 被更新
// value=[a], value2=[1 2 c d] -- ok: value 未改变, value2 被更新
// 原因:slice1容量不够,value2利用append添加元素后,value2的底层数组将会重新分配内存和大小(与slice1内存地址不同)
slice10 := make([]string, 1, 10) // 长度 1, 容量 10
slice10[0] = "a"
doStuff(slice10)
// Output:
// value=[a] -- ok
// value=[a], value2=[a b c d] -- ok: value 未改变, value2 被更新
// value=[1], value2=[1 2 c d] -- value 改变了???
// 原因:slice10容量充足,value2利用append添加元素后,value2的底层数组不会重新分配内存和大小,与(与slice10内存地址相同)
}