
go 中使用切片的切片([][]t)模拟二维数组时,若重复复用同一底层数组的切片变量,append 操作会因共享底层数组而导致所有行被最后读入的数据覆盖——根本原因是切片是引用类型,包含指向底层数组的指针。
go 中使用切片的切片([][]t)模拟二维数组时,若重复复用同一底层数组的切片变量,append 操作会因共享底层数组而导致所有行被最后读入的数据覆盖——根本原因是切片是引用类型,包含指向底层数组的指针。
在 Go 中,切片(slice)并非值类型,而是包含三个字段的结构体:指向底层数组的指针、长度(len)和容量(cap)。当你执行 array = append(array, row) 时,实际追加的是对 row 底层数组的引用。如果后续修改了 row 的元素(例如再次调用 fmt.Scanf 写入新值),而该 row 仍被之前 append 进去的切片所共享,则所有已存入 array 的行都会“意外”更新——因为它们指向同一块内存。
在原始代码中,row := make([]int, n) 仅在循环外声明一次,导致所有迭代都复用同一个 row 切片。尽管每次循环内重新扫描输入,但 append(array, row) 总是把同一个 slice header(含相同底层数组指针)压入 array,最终 array 中所有子切片均指向最后一轮填充的 [3 4],因此输出为 [[3 4] [3 4]]。
✅ 正确做法是在每次外层循环中重新创建独立的切片,确保每行拥有专属的底层数组:
func main() {
var n int
fmt.Scanf("%d", &n)
array := [][]int{}
for i := 0; i < n; i++ {
row := make([]int, n) // ✅ 每次迭代新建切片,隔离底层数组
for j := range row {
fmt.Scanf("%d", &row[j])
}
fmt.Println("Printing current Row", row)
array = append(array, row) // 此时追加的是独立副本
fmt.Println("Printing current Array", array)
}
fmt.Println("Final Array", array)
}⚠️ 注意事项:
- 不要提前分配 row 在循环外;务必在 for 循环体内 make 新切片;
- 若需高性能或已知尺寸,也可一次性分配总内存并手动切分(如 data := make([]int, n*n),再用 data[i*n : i*n+n] 构造每行),但语义清晰性不如上述方式;
- append 本身无错,问题本质是误用了可变的引用对象——这是 Go 切片机制的核心特性,而非 bug。
掌握这一原理,不仅能修复二维切片问题,更能避免在传递切片、缓存切片或并发写入等场景中出现隐蔽的共享状态错误。

















