go切片是含指针、长度、容量的结构体,共享底层数组内存;修改一个切片元素会影响其他指向同一数组重叠区域的切片;需用append([]int{}, s…)或make+copy主动断开共享。

这意味着:多个切片(或切片与原数组)可能指向同一块内存,修改其中一个的元素,其他变量会同步看到变化——不是“看起来像”,而是真正在改同一地址上的值。
切片本身就是一个带指针的描述符
Go 中的切片不是数据容器,而是一个三字段结构体:指向底层数组的指针 + 长度 + 容量。这个指针是真实内存地址,决定了它和谁共享数据。
- 用
arr[1:4]截取,新切片的指针仍指向arr的第 2 个元素位置 - 用
make([]int, 5)创建,指针指向一块全新分配的内存,不与任何已有变量共享 - 函数传参时传递切片,实际是复制这个三字段结构体,但其中的指针值不变,所以仍连着原数组
共享带来的典型影响
只要两个切片的指针落在同一底层数组的重叠范围内,它们就构成“可互相修改”的关系。
-
arr := []int{1,2,3,4,5},s1 := arr[1:3],s2 := arr[2:4]→ 修改s1[1]就等于改s2[0] - 长期持有某个小切片(如
logLines[0:1]),可能导致整个大日志数组无法被 GC 回收 - 并发 goroutine 同时写不同但重叠的切片,可能引发数据竞争(race condition)
如何主动断开共享
想让新切片完全独立,必须让它指向新分配的内存,不能依赖截取或赋值。
- 用
append([]int{}, s...):简洁安全,底层会新建数组并拷贝 - 用
make+copy:更明确控制长度和容量,适合预分配场景 - 避免只靠
append(s, x)来“隔离”——扩容不一定发生,即使发生,原切片也未受影响,但新旧切片是否分离不可控
基本上就这些。共享是 Go 切片高效的基础,也是 bug 的常见源头;关键不在“能不能共享”,而在“要不要共享”。