
在 Go 中遍历二维切片时,直接对 range 循环中的变量赋值无法修改原切片元素,因为 range 提供的是元素副本而非引用;必须通过索引(如 slice[i][j])显式赋值才能生效。
在 go 中遍历二维切片时,直接对 range 循环中的变量赋值无法修改原切片元素,因为 range 提供的是元素副本而非引用;必须通过索引(如 `slice[i][j]`)显式赋值才能生效。
Go 的 range 关键字在遍历切片时,会将每个元素复制到循环变量中,而非提供对该元素的引用或指针。这意味着对循环变量(如 y 或 x)的赋值,仅修改了该临时副本,而不会影响原始二维切片 pic 的结构或内容。
例如,以下代码看似在初始化二维切片,实则完全无效:
func Pic(dx, dy int) [][]uint8 {
pic := make([][]uint8, dy) // 创建长度为 dy 的外层切片,但每个元素为 nil
for _, y := range pic { // y 是 pic[i] 的副本(初始为 nil)
y = make([]uint8, dx) // 仅给 y 赋新切片,未写入 pic[i]
for _, x := range y { // x 是 y[j] 的副本(uint8 值类型)
x = uint8(1) // 修改 x 对 y[j] 无任何影响
}
}
return pic // 返回的是 dy 个 nil 切片组成的切片,访问 pic[i][j] 必然 panic
}
执行 Pic(3, 3) 后,pic 实际为 [][]uint8{nil, nil, nil} —— 外层数组已分配,但每个内层切片仍为 nil。后续若尝试 pic[0][0],即对 nil 切片索引,触发 panic: runtime error: index out of range。
✅ 正确做法是:始终通过索引显式赋值,确保修改作用于原始结构:
func Pic(dx, dy int) [][]uint8 {
pic := make([][]uint8, dy)
for i := range pic { // i 是索引,pic[i] 可被修改
pic[i] = make([]uint8, dx) // 初始化第 i 行
for j := range pic[i] { // j 是列索引
pic[i][j] = uint8(i * j) // 支持任意计算逻辑,如 i*j、i+j 等
}
}
return pic
}
⚠️ 注意事项:
- range 的值变量(y, x)是只读副本,不可用于反向写入原切片;
- 若需修改结构(如分配内层切片),必须使用索引 pic[i] = ...;
- 若需修改元素值(如 uint8),同样必须用 pic[i][j] = ...,而非 x = ...;
- 对于引用类型(如 *struct),range 得到的是指针副本,解引用后可修改其指向对象,但切片本身仍是值类型,其底层数组头信息(ptr, len, cap)不共享。
总结:Go 中切片是描述性值类型,range 遍历时传递的是其副本。要构建或填充二维切片,请坚持“索引驱动赋值”原则——这既是安全实践,也是 Go 语言内存模型的自然体现。











