代码:
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设计者选择不将原始切片的底层数组更改为新切片的底层数组,以便使两个切片仍具有相同的底层数组?在当前状态下,当我更改新重新切片的某些元素的值时,我必须检查是否更改了基础数组以确定此操作是否对其备份的其他切片有副作用(请参阅输出的最后两行)。我认为这很尴尬。
答案 0 :(得分:3)
但是为什么golang设计者选择不将原始切片的底层数组更改为新切片的底层数组,以便使两个切片仍然具有相同的底层数组?
主要是,相同数组的切片可以绝对出现在程序的任何位置 - 完全不同的函数,包等。鉴于切片如何在记忆中布局,Go必须"找到"所有切片共享数组以更新它们;它没办法。
其他一些数组列表实现(比如Python列表)的方法是,你传递的内容实际上是一个指针,就像Go切片一样,如果两个变量保持"相同的列表",当你看另一个变量时,使用一个变量的追加也会出现。这也有一些效率成本 - 另一个指针查找a[0]
。在那些你真的需要在这里附加作为附加物的情况下,你可以使用指向切片的指针。
指向切片的指针如果你需要它会给你带锯齿但是不提供拼接 - 为了得到你要求的所有,你需要一个不同的安排,我可以&# 39;想想野外的一个例子(偏移,长度和指向struct { capacity int; firstElem *type }
的指针)。