go - 在 Go 中获取 slice 元素的地址是否意味着该元素的副本?

标签 go pointers struct slice

比方说 Go 1.18程序有一个相当重 struct ,复制被认为是昂贵的:

type MyStruct struct {
    P string
    // a lot of properties
}

现在让我们定义一个函数,将此类元素的 slice 作为输入参数,其目标是更新每个 slice 元素的属性:

func myFunc(sl []MyStruct) {
    for i := range sl {
        p := &sl[i]       // <-- HERE
        p.P = "bar"
        // other properties mutations
    }
}

<-- HERE标记,Golang 编译器是否将 slice 元素临时复制到循环范围中,或者是否就地获取 slice 元素的地址?

这个想法是避免复制整个 slice 元素。

一个工作示例:https://go.dev/play/p/jHOC2DauyrQ?v=goprev

最佳答案

&sl[i] 不会复制 slice 元素,它只是计算第 ith 元素的地址。

slice 元素充当变量,&x 计算结果为 address x 变量的。想一想:既然&sl[i]是第i个元素的地址,该地址不需要也不使用结构体值,为什么要复制呢?

如果您的 slice 太大,以至于您担心(隐式)副本对性能的影响,那么您确实应该首先考虑在 slice 中存储指针,这样您就可以使循环和访问元素变得更加方便更简单,无需担心副本:

func myFunc(sl []*MyStruct) {
    for _, v := range sl {
        v.P = "bar"
        // other properties mutations
    }
}

另请注意,如果您的 slice 包含非指针,并且您想要更改 slice 元素的字段,则索引 slice 并引用该字段也不涉及复制结构元素:

func myFunc(sl []MyStruct) {
    for i := range sl {
        sl[i].P = "bar"
        // other properties mutations
    }
}

是的,如果您必须修改多个字段,这可能会更冗长并且效率可能会降低(但编译器也可能会识别并优化多个 sl[i] 表达式的计算)。

关于go - 在 Go 中获取 slice 元素的地址是否意味着该元素的副本?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74459213/

相关文章:

c++ - 数组指针的影响

c - 我是否创建一个临时 c 结构并通过套接字发送它?

c - 使用 C 语言的文本文件登录系统

logging - 以编程方式关闭日志记录 revel 框架

go - 测试函数以获得 100% 的覆盖率

Google Cloud HTTP(S) 负载平衡器不会取消与后端的连接

c# - 为什么在 C# 中将结构与 NULL 进行比较是合法的?

go - 性能几乎以线性方式提高,将GOMAXPROCS从1增加到4,但此后保持平稳

c++ - 指针和方法的奇怪用法

pointers - slice 结构行为