
Go 的 slice 是引用类型,append 时若重复添加同一 slice 变量,实际是多次添加指向同一底层数组的指针,导致后续修改相互覆盖。
go 的 slice 是引用类型,`append` 时若重复添加同一 slice 变量,实际是多次添加指向同一底层数组的指针,导致后续修改相互覆盖。
在 Go 中,slice 并非值类型,而是一个包含三个字段的结构体:指向底层数组的指针、长度(len)和容量(cap)。这意味着当你执行 append(slice2, slice) 时,传递的不是 slice 当前元素的副本,而是其底层数据的引用。
回顾原始代码:
slice := []int{0, 0}
slice2 := [][]int{}
for i := range slice {
slice[0] = i
slice[1] = i
slice2 = append(slice2, slice) // ⚠️ 每次都添加同一个 slice 变量的引用
}
fmt.Println(slice2) // 输出:[[1 1] [1 1]]
该循环共执行两次(i = 0 和 i = 1):
- 第一次:
slice被修改为[0 0],然后追加到slice2—— 此时slice2[0]指向底层数组[0 0]; - 第二次:
slice被修改为[1 1],再次追加 —— 但slice2[0]和slice2[1]仍指向同一块内存,因为slice变量本身未变,只是其内容被覆盖。
因此最终 slice2[0] 和 slice2[1] 都反映最后一次写入的结果 [1 1]。
✅ 正确做法:每次迭代创建独立的新 slice,确保每个子切片拥有自己的底层数组:
func main() {
slice2 := [][]int{}
for i := range [2]int{} { // 等价于 i = 0, 1
ns := []int{i, i} // ✅ 每次分配新底层数组
slice2 = append(slice2, ns)
}
fmt.Println(slice2) // 输出:[[0 0] [1 1]]
}
? 进阶提示:若需深拷贝已有 slice,可使用 copy 或切片字面量复制:
// 基于原 slice 复制(安全但略冗余) ns := make([]int, len(slice)) copy(ns, slice) ns[0], ns[1] = i, i slice2 = append(slice2, ns)
⚠️ 注意事项:
- 不要复用外部 slice 变量作为
append的元素,尤其在循环中; - 使用
fmt.Printf("%p", &slice[0])可验证多个 slice 是否共享底层数组地址; - 若需高性能且子切片较短,预分配
slice2并配合make([][]int, 0, n)可避免多次扩容。
理解 slice 的引用语义,是写出可预测、无副作用 Go 代码的关键基础。










