
在 Go 中,对 map 进行 range 遍历时,循环变量 v 是复用的同一内存地址;若直接取其地址存入切片,所有指针将指向最终迭代结束时的 v 值,导致数据“意外统一”。
在 go 中,对 map 进行 range 遍历时,循环变量 `v` 是复用的同一内存地址;若直接取其地址存入切片,所有指针将指向最终迭代结束时的 `v` 值,导致数据“意外统一”。
当你遍历一个 map 并试图将每个 value 的地址保存到 []*int 切片中时,看似逻辑清晰,实则暗藏陷阱。根本原因在于:Go 的 for range 循环中,value 变量(如本例中的 v)在整个循环生命周期内只声明一次,且每次迭代仅对其赋新值,而非重新分配内存。
这意味着:
- 所有
&v实际都指向同一个栈地址; - 循环结束后,
v保留最后一次迭代的值(例如912); - 因此,切片中所有指针解引用后均得到该最终值。
以下是最小可复现实例:
func TestMapValuePointer2(t *testing.T) {
m := map[string]int{"rsc": 3711, "r": 2138, "gri": 1908, "adg": 912}
array := make([]*int, 0, len(m))
for _, v := range m {
array = append(array, &v) // ❌ 错误:反复取同一变量地址
}
for _, ptr := range array {
fmt.Printf("Value: %d\n", *ptr) // 输出全部为 912(或其它末次值)
}
}✅ 正确做法:在循环体内显式创建新变量副本,确保每次迭代拥有独立地址:
for _, v := range m {
vCopy := v // ✅ 新变量,独立内存位置
array = append(array, &vCopy)
}或者更简洁地使用短变量声明(效果等价):
for _, v := range m {
v := v // 显式遮蔽外层 v,强制分配新空间
array = append(array, &v)
}⚠️ 注意事项:
- 此问题不仅限于 map,对 slice 的
for range遍历中取&v同样适用; - 若无需修改原始值,直接存储
int(而非*int)是最简单、最安全的选择; - 若确实需要指针语义(如后续动态更新),务必确保所指向的变量生命周期覆盖使用期——此处
vCopy是栈上局部变量,但因其被指针捕获且切片在函数内使用,是安全的;若需返回该切片,则必须确保值被复制到堆(Go 编译器通常会自动逃逸分析并完成堆分配)。
总结:Go 的循环变量复用机制提升了性能,但也要求开发者明确内存模型。遇到“所有指针指向同一值”的现象,第一反应应是检查是否误用了循环变量地址——通过一次显式拷贝即可破局。

















