
在 Go 的 range 循环中,迭代变量 v 是原切片元素的副本,对其取地址(&v)得到的是该临时变量的地址,而非原切片中对应元素的地址;因此修改 *p 不会影响原数据。
在 go 的 range 循环中,迭代变量 `v` 是原切片元素的**副本**,对其取地址(`&v`)得到的是该临时变量的地址,而非原切片中对应元素的地址;因此修改 `*p` 不会影响原数据。
在 Go 语言中,range 语句遍历切片(或数组、map)时,每次迭代都会将当前元素复制给循环变量 v。这意味着 v 是一个独立的局部变量,其生命周期仅限于当前迭代轮次,且与原切片中的元素在内存中是分离的。因此,对 &v 取地址并赋值给指针 p,实际上是指向了这个临时副本——后续通过 p.state = "3" 修改的只是该副本的字段,原切片中的数据完全不受影响。
这一点在嵌套结构体操作中尤为容易被忽略,正如示例代码所示:
for _, v := range a.Barry[0].Carry {
if v.state == "2" {
p = &v // ❌ 错误:&v 指向的是循环副本,非原切片元素
}
}
p.state = "3" // 此处修改无效,a 未变化✅ 正确做法是:使用索引访问原切片,再对其元素取地址:
for i, v := range a.Barry[0].Carry {
if v.state == "2" {
p = &a.Barry[0].Carry[i] // ✅ 正确:&slice[i] 指向原切片中第 i 个元素的真实地址
}
}
p.state = "3" // 现在能成功更新原始结构? 小贴士:即使你不需要 v 的值,也建议保留 i 索引(用 _ 忽略 v)来明确意图:
for i := range a.Barry[0].Carry { if a.Barry[0].Carry[i].state == "2" { p = &a.Barry[0].Carry[i] } }
此外,还需注意:
- 切片本身是引用类型,但其元素仍按值传递(除非元素本身就是指针类型);
- 若切片元素为指针(如 []*C),则 range 中的 v 是指针副本,&v 是指向该指针变量的地址(即 **C),通常这不是你想要的——此时应直接使用 v(即 *C),或仍用索引确保操作目标明确。
总结:永远不要对 range 的值变量 v 取地址来期望修改原切片内容;请改用索引 &slice[i] 获取真实元素地址。 这是 Go 值语义的核心体现,也是避免“看似修改却无效”问题的关键原则。


















