Go中range遍历数组或切片时,v是元素值拷贝,修改v不影响原数组;需通过索引i(如a[i])修改;for v := range a实为获取索引,易误用。

range 遍历数组时,v 是值拷贝,修改它不影响原数组
Go 中用 range 遍历数组(或切片)时,每次迭代的第二个变量(常写作 v)是当前元素的**副本**,不是引用。这意味着对 v 的赋值不会改变原数组内容。
常见错误现象:写类似 v = 99 后发现数组没变;或者误以为 &v 能拿到原元素地址——其实它只是副本的地址。
- 数组字面量或变量直接遍历时,
v类型就是元素类型(如int),且独立于原数组内存 - 若需修改原数组,必须通过索引:用
range获取的i去写a[i] = ... - 对大结构体元素,值拷贝可能有性能开销;若只读,可考虑用指针切片避免复制(但注意生命周期)
获取索引必须显式声明第一个变量,不能省略
range 返回两个值:索引和值。如果只写一个变量,Go 默认把索引赋给它,不会自动跳过索引只取值。这是新手高频踩坑点。
错误写法:for v := range a { ... } —— 这里的 v 实际是索引,不是元素值,容易引发逻辑错乱。
立即学习“go语言免费学习笔记(深入)”;
- 要同时用索引和值:写成
for i, v := range a - 只要值、不要索引:用下划线占位,
for _, v := range a - 只要索引、不要值:写成
for i := range a(这是合法且高效的写法)
遍历过程中修改数组长度不影响当前循环次数
数组长度在编译期固定,不可变;但若遍历的是切片([]T),而你在循环中调用 append 或重切(s = s[:n]),不会影响已启动的 range 迭代次数。
原因:range 在开始时就已根据切片当时的 len 和底层数组确定了迭代范围,后续修改切片头尾不改变这次循环的“计划”。
- 修改切片长度(如
s = append(s, x))可能导致底层数组扩容,但原 range 仍按旧长度跑完 - 在循环内修改
s[i]是安全的,因为索引仍在有效范围内 - 但若循环中
append导致扩容,且你又用&s[i]保存地址,要注意那些指针可能指向被遗弃的旧底层数组
数组与切片的 range 行为一致,但底层机制不同
虽然语法一样,但数组和切片的 range 底层处理不同:数组遍历直接按字面长度展开,而切片遍历依赖其当前 len 字段。这点在反射或汇编层面可见,但对日常编码影响不大。
真正需要注意的是:当你把数组传给函数,形参若声明为 [3]int,它是值传递;若声明为 []int,传的是切片头(含指针、len、cap)。这会直接影响你在函数内 range 时能否修改调用方数据。
- 函数接收
[N]T:内部range操作的是副本,外部数组完全不受影响 - 函数接收
[]T:内部range的v仍是副本,但通过i修改s[i]可影响外部底层数组 - 别试图用
range来“检测数组是否被修改”——它不感知外部并发写,也不提供原子性保证
for v := range a 这种写法的真实含义,以及在循环里混用 append 和索引赋值时的底层数组稳定性。这两处一旦出错,调试成本远高于提前多打一个下划线或查一眼 len。










