猿问

为什么golang slice 内部是这样设计的?

代码:


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 %v\n",

        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的设计者选择不将原始切片的底层数组改成新切片的底层数组,从而让两个切片仍然拥有相同的底层数组呢?在当前状态下,当我更改新重新切片的某些元素的值时,我必须检查是否更改了底层数组以决定此操作是否对它支持的其他切片有副作用(请参阅输出的最后两行)。我觉得这很尴尬。


慕桂英546537
浏览 156回答 1
1回答

慕桂英4014372

但是为什么golang的设计者选择不将原始切片的底层数组改成新切片的底层数组,从而让两个切片仍然拥有相同的底层数组呢?主要是,同一个数组的切片可以出现在程序中的任何地方——完全不同的函数、包等等。考虑到切片在内存中的布局方式,Go 必须“找到”共享数组的所有切片来更新它们;它没有办法。其他一些数组列表实现(如 Python 列表)的方法是,您传递的实际上是一个指向Go 切片之类的指针,如果两个变量保存“相同的列表”,则使用一个变量的附加也将显示当你看着另一个的时候。这也有一些效率成本——另一个指针查找要做a[0]。在那些你真的需要一个附加在这里作为附加在那里的情况下,你可以使用指向切片的指针。如果需要,指向切片的指针会为您提供别名,但不提供子切片 - 要获得您要求的所有内容,您需要一种不同的安排,我无法想到野外的示例(偏移量,长度, 和指向struct { capacity int; firstElem *type }) 的指针。
随时随地看视频慕课网APP

相关分类

Go
我要回答