
在 Go 中,对 map 进行 range 遍历时,循环变量 v 是单个复用变量,每次迭代仅更新其值;若取其地址存入切片,所有指针最终都指向同一内存位置,导致读取时全部显示最后一次迭代的值。
在 go 中,对 map 进行 range 遍历时,循环变量 `v` 是**单个复用变量**,每次迭代仅更新其值;若取其地址存入切片,所有指针最终都指向同一内存位置,导致读取时全部显示最后一次迭代的值。
这是一个典型的 Go 循环变量作用域与内存复用问题。核心在于:range 语句中声明的循环变量(如 v)在整个循环生命周期内只分配一次内存,后续每次迭代只是对其赋新值,而非创建新变量。
来看原始代码的问题所在:
for _, v := range m {
array[i] = &v // ❌ 总是取同一个变量 v 的地址
i++
}
虽然 m 包含四个不同整数值(3711、2138、1908、912),但 v 在整个循环中始终是同一个栈变量。假设底层遍历顺序为 "rsc"→"r"→"gri"→"adg",则 v 的值依次变为 3711 → 2138 → 1908 → 912,而 &v 始终返回该变量的固定地址。最终 array 中四个元素全部存储了这个相同地址——因此解引用时均读到 v 的最终值 912(具体输出值取决于 map 遍历顺序,但必为最后一个赋值)。
✅ 正确做法:在循环体内显式创建独立副本,确保每个指针指向不同的内存:
for _, v := range m {
vv := v // ✅ 每次迭代新建局部变量 vv
array[i] = &vv // ✅ 每个 &vv 指向独立地址
i++
}
此时 vv 是每次迭代中声明的新变量(位于栈上不同位置或经逃逸分析分配至堆),&vv 得到的是各自唯一的地址,解引用即可获得预期的原始值。
⚠️ 注意事项:
- 此行为不仅限于
map,slice、channel等range遍历同样适用; - 若需保存指针,应避免直接取循环变量地址,优先考虑复制值或使用索引访问原数据(如
&m[key]—— 但 map value 不可寻址,此方式不适用,故必须复制); - 从设计角度思考:是否真的需要
[]*int?大多数场景下直接存储[]int更安全、高效且符合 Go 的惯用法;指针引入额外复杂度,仅在需动态修改原始值或优化大对象拷贝时才值得采用。
总结:理解 Go 中 range 变量的复用机制是避免此类“幽灵指针”陷阱的关键。牢记——循环变量是共享的,要存地址,先做副本。










