为什么戈朗切片内部设计成这样

  • 本文关键字:内部 切片 go
  • 更新时间 :
  • 英文 :


代码:

func main() {
    a := []int{1, 2}
    printSlice("a", a)
    b := a[0:1]
    printSlice("b origin", b)
    b = append(b, 9)
    printSlice("b after append b without growing capacity", b)
    printSlice("a after append b without growing capacity", a)
    b = append(b, 5, 7, 8)
    printSlice("a after append b with grown capacity", a)
    printSlice("b after append b with grown capacity", b)
    b[0] = 1000
    printSlice("b", b)
    printSlice("a", a)      
}
func printSlice(s string, x []int) {
    fmt.Printf("%s len=%d cap=%d %vn",
        s, len(x), cap(x), x)
}

输出:

a len=2 cap=2 [1 2]
b origin len=1 cap=2 [1]
b after append b without growing capacity len=2 cap=2 [1 9]
a after append b without growing capacity len=2 cap=2 [1 9]
a after append b with grown capacity len=2 cap=2 [1 9]
b after append b with grown capacity len=5 cap=6 [1 9 5 7 8]
b len=5 cap=6 [1000 9 5 7 8]
a len=2 cap=2 [1 9]

有趣的是最后两行印刷。我已经知道切片只是底层数组的一个窗口。当在容量内重新切片时,两个切片共享相同的底层数组,但是当我重新切片以超出其容量时,两个切片具有不同的底层数组。但是,为什么 golang 设计者选择不将原点切片的底层数组更改为新切片的底层数组,从而使两个切片仍然具有相同的底层数组呢?在当前状态下,当我更改新重新切片切片的某些元素的值时,我必须检查我是否更改了底层数组,以确定此操作是否对它备份的其他切片有副作用(请参阅输出的最后两行)。我觉得这很尴尬。

但是,为什么 golang 设计者选择不将原点切片的底层数组更改为新切片的底层数组,从而使两个切片仍然具有相同的底层数组呢?

主要是,同一数组的切片可以绝对出现在程序中的任何位置 - 完全不同的函数、包等。考虑到切片在内存中的布局方式,Go 必须"找到"共享数组的所有切片来更新它们;它没有办法。

其他一些数组列表实现(如 Python 列表)的方法是,你传递的内容实际上是指向 Go 切片之类的东西的指针,如果两个变量持有"相同的列表",则当您查看另一个变量时,也会显示使用一个变量的追加。这也有一些效率成本 - 另一个指针查找来做a[0]。在确实需要在此处追加以充当该处的追加的情况下,可以使用指向切片的指针。

如果你想要它,指向切片的指针会给你别名,但不提供子切片 - 为了获得你要求的一切,你需要一个不同的排列,我想不出一个野外的例子(偏移量、长度和指向struct { capacity int; firstElem *type }的指针)。

最新更新