
在 Go 中对 map 进行 range 遍历时,若直接取循环变量 v 的地址存入切片,所有指针将指向同一内存地址,导致最终读取时全部显示为最后一次迭代的值。这是因循环变量复用机制所致,需通过变量拷贝规避。
在 go 中对 map 进行 range 遍历时,若直接取循环变量 `v` 的地址存入切片,所有指针将指向同一内存地址,导致最终读取时全部显示为最后一次迭代的值。这是因循环变量复用机制所致,需通过变量拷贝规避。
Go 的 for range 循环在遍历 map(或 slice、channel)时,循环变量 v 是在循环开始前一次性声明,并在每次迭代中被重新赋值,而非每次创建新变量。这意味着所有 &v 实际上都取的是同一个变量 v 的地址——该地址在循环结束后稳定指向最后一次赋值的结果(如示例中为 912),因此切片中所有 *int 指针解引用后均输出相同值。
以下代码重现了该问题:
func TestMapValuePointer2(t *testing.T) {
m := map[string]int{"rsc": 3711, "r": 2138, "gri": 1908, "adg": 912}
n := len(m)
array := make([]*int, n)
i := 0
for _, v := range m {
array[i] = &v // ❌ 错误:所有元素指向同一个 v
i++
}
for _, ptr := range array {
fmt.Printf("Value: %d\n", *ptr) // 输出全为 912(取决于最后迭代值)
}
}✅ 正确做法:在循环体内显式创建新变量,确保每次迭代拥有独立的内存空间:
for _, v := range m {
vv := v // ✅ 创建局部副本
array[i] = &vv // 现在每个 &vv 指向不同的 int 实例
i++
}此时每个 &vv 指向各自独立的栈变量,输出将符合预期(顺序可能随机,但数值各不相同)。
⚠️ 注意事项:
- Go 编译器不会报错或警告此类逻辑错误,需开发者主动识别;
- 该行为不仅影响 map,同样适用于 slice 和 channel 的
range遍历; - 若无需修改原始值,直接存储
int值(即[]int)更安全、高效,避免指针管理开销; - 若必须使用指针(如需后续动态更新值),还可考虑将值先复制到堆(如
new(int)或&m[key]),但需注意 map 元素本身不可取地址(&m[key]合法,但&v不合法)。
总结:理解 Go 循环变量的复用本质是避免此类“幽灵指针”问题的关键;始终牢记——&v 不代表“当前值的地址”,而是“循环变量 v 当前绑定位置的地址”。

















