在 Go 中复制结构的切片参数

Copying a struct's slice parameter in Go

假设我有一个带有如下切片参数的结构,我创建了一个并用一些值填充它:

type s struct {
  sl []float32
}

func NewS() *s {
  return &s{
    sl: make([]float32, 3),
  }
}

func main() {
  a := NewS()
  a.sl[0] = 1
  a.sl[1] = 2
  a.sl[2] = 3

  b := NewS()
  // Code here
}

我希望 b 具有与 a.sl 具有相同值的 sl,特别是,我想了解如何以不同的方式做到这一点:

  1. 其中整个 b 指向整个 a(这样如果结构中还有其他参数,它们都会匹配)
  2. 其中 b.sl 将指向 a.sl
  3. 作为深拷贝,其中没有指针(并且不是通过遍历切片完成的,而是通过一种无论类型如何都可重复的方式,理想情况下)

以下是我迄今为止尝试和发现的内容。

b = a
fmt.Printf("a: %p\n", &a)
fmt.Printf("b: %p\n", &b)
fmt.Printf("a.sl: %p\n", &a.sl)
fmt.Printf("b.sl: %p\n", &b.sl)

它为结构输出不同的内存地址,但为切片输出相同的内存地址。这是为什么?如果 a 只是指向 b,为什么结构地址也不相同?

a: 0xc00000e030
b: 0xc00000e038
a.sl: 0xc000054040
b.sl: 0xc000054040

然后我尝试了:

b.sl = a.sl
// ...

输出所有不同的内存地址。 ab的地址不同当然有道理,但是为什么b.sl不指向a.sl的地址呢?

a: 0xc00000e028
b: 0xc00000e030
a.sl: 0xc00000c030
b.sl: 0xc00000c048

最后我尝试了:

copy(b.sl, a.sl)
// ...

输出与上面类似。这个结果是我唯一期望的结果,因为我希望它能进行深度复制。

a: 0xc0000b8018
b: 0xc0000b8020
a.sl: 0xc0000ae018
b.sl: 0xc0000ae030

您能帮我了解这 3 种情况下发生的情况以及其他实现我想要的结果并执行各种类型复制的方法吗?

这里有一些背景信息:切片包含指向切片后备数组的指针、切片的长度和后备数组的容量。 (指针,长度,容量)被称为slice header

切片的地址是切片的地址 header,而不是指向切片后备数组或切片后备数组第一个元素的指针。

b=a

语句执行后变量ab指向同一个值

变量 a 和 b 不同,因此具有不同的地址。

表达式&a.sl是指向值中sl字段的地址。表达式 &b.sl 等于 &a.sl 因为 ab 指向相同的值。

这是您的第 1 点:a 一个 b 相同值的点。

b.sl = a.sl

该语句将切片 header 从 a.sl 复制到 b.sl。切片具有不同的地址,因为切片 headers 保持不同。

这与您的 #2 很接近:a.sl 一个 b.sl 点位于同一后备阵列。对 a.sl 中元素的修改通过 b.sl 可见,但对切片 header a.sl 的更改不会反映在`b.sl.[=32= 中]

复制(b.sl, a.sl)

该语句将 a.sls 支持数组中的元素复制到 b.sl

这是您的#3,深拷贝。 a 中的更改不会反映在 b 中。

切片是一个三元组,包含指向底层数组、长度和容量的指针。当您将一个切片分配给另一个切片时,您分配了那个三元组。底层数组保持不变。

切片是数组的视图。如果你需要一个切片的深拷贝,你必须创建另一个切片并自己复制它。

如果您将一个数组分配给另一个数组,则会将源的每个元素复制到目标。