比方说 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 元素。
最佳答案
&sl[i]
不会复制 slice 元素,它只是计算第 i
th 元素的地址。
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/