Go 切片slice复制的陷阱 - 底层数组重新分配内存和大小

切片带来了很多问题,考虑到性能原因,将一个切片进行切片,不会复制底层的数组。这意味着切片的子切片只是遵循原始切片变化的视图。


因此,如果想要将它与初始的切片分开请不要忘记 使用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内存地址相同)
}

猜你喜欢

转载自blog.csdn.net/QiuHaoqian/article/details/108996719