17370845950

如何在 Go 中正确从切片获取结构体指针

在 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 {
        fm

t.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 的值拷贝语义,是避免此类陷阱的关键。