
在 go 中,for range 遍历 map 时复用循环变量 v,导致取其地址存入切片后所有指针均指向同一个内存位置,最终解引用时得到重复的末次赋值结果。
在 go 中,for range 遍历 map 时复用循环变量 v,导致取其地址存入切片后所有指针均指向同一个内存位置,最终解引用时得到重复的末次赋值结果。
Go 的 for range 循环在遍历 map(或 slice、channel)时,循环变量是复用的——即整个循环过程中只声明一次 v,每次迭代仅更新其值,而非创建新变量。因此 &v 始终取到的是同一个变量 v 的地址。当该地址被多次存入切片 array 后,切片中所有 *int 元素实际都指向同一块内存;而该内存最后存储的是循环最后一次迭代的值(例如 912),于是所有 *k 解引用结果均为 912。
以下是最小可复现示例:
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 _, k := range array {
fmt.Printf("Value: %d\n", *k) // 输出四个相同的数字(如 912)
}
}✅ 正确做法:在循环体内显式创建新变量,确保每次迭代都有独立的内存地址:
for _, v := range m {
vv := v // ✅ 新变量,每次迭代分配独立栈空间
array[i] = &vv
i++
}此时每个 &vv 指向不同地址,解引用可正确还原原始 map 值。
⚠️ 注意事项:
- 此行为不仅限于 map,对 slice 或 channel 的
range遍历同样适用; - 若需保存值的地址,务必避免直接取循环变量地址;
- 大多数场景下,直接存储值(如
[]int)比存储指针更安全、高效;仅当需要修改原值或处理大结构体时才考虑指针; - 使用
go vet可检测此类潜在问题(虽非默认启用,但建议在 CI 中开启vet -shadow等检查)。
总结:Go 的循环变量复用机制提升了性能,但也要求开发者明确其生命周期。理解 v 是“单变量多次赋值”而非“多变量依次声明”,是避免此类陷阱的关键。

















