
在 go 中,使用 range 遍历切片时,value 是副本而非原元素引用;直接取 &value 会得到同一个局部变量地址,导致所有指针指向同一内存位置。正确方式是通过索引取址:&slice[i]。
Go 的 range 循环在遍历切片时,会将每个元素复制到循环变量 value 中(而非传递引用),因此 &value 始终是该局部变量在栈上的固定地址——无论迭代多少次,这个地址都不变。这正是你观察到所有指针值相同的根本原因。
要获取切片中各结构体元素的真实地址,必须绕过 value 副本,直接对底层数组元素取址。以下是修正后的完整示例:
package main
import "fmt"
type demo struct {
name string
}
func main() {
demoSlice := make([]demo, 3)
demoSlice[0] = demo{"str1"}
demoSlice[1] = demo{"str2"}
demoSlice[2] = demo{"str3"}
pointSlice := make([]*demo, 3)
for i := range demoSlice {
fmt.Printf("{%s}==++++++++++++++%p\n", demoSlice[i], &demoSlice[i])
pointSlice[i] = &demoSlice[i]
}
// 验证指针有效性
fmt.Println("\n通过指针访问值:")
for i, p := range pointSlice {
fmt.Printf("pointSlice[%d] -> %+v\n", i, *p)
}
}输出示例(地址因运行环境而异,但彼此不同):
{str1}==++++++++++++++0x1040a120
{str2}==++++++++++++++0x1040a128
{str3}==++++++++++++++0x1040a130
通过指针访问值:
pointSlice[0] -> {name:str1}
pointSlice[1] -> {name:str2}
pointSlice[2] -> {name:str3}⚠️ 注意事项:
- 切片底层是数组,&slice[i] 安全有效,前提是 i 在合法范围内;
- 若后续对 demoSlice 执行 append 操作导致底层数组扩容,原有指针可能失效(指向已废弃内存),此时应避免长期持有此类指针;
- 若需动态增长且保证指针稳定性,可考虑使用 *demo 切片直接初始化,或改用 map/自定义容器管理结构体指针。
总结:Go 中“取切片元素指针”的标准写法永远是 &slice[index],而非 &value;理解 range 的值拷贝语义,是避免此类陷阱的关键。










