这是一些 Go 书籍中的代码片段。
func incr(s []int) {
s = append(s, 0)
for i := range s {
s[i]++
}
}
func main() {
s1 := []int{1, 2}
s2 := s1
s2 = append(s2, 3)
incr(s1)
incr(s2)
fmt.Print(s1, s2) // "[1, 2][2, 3, 4]"
}
Run Code Online (Sandbox Code Playgroud)
我不明白为什么结果是“[1, 2][2, 3, 4]”。
基于https://golang.org/doc/ effective_go#slices 中的两点:
切片保存对基础数组的引用,如果将一个切片分配给另一个切片,则两个切片都引用同一个数组
如果函数采用切片参数,则调用者可以看到它对切片元素所做的更改,类似于将指针传递给底层数组
这是我想象应该发生的事情:
incr(s1),s1 被追加 0,所有项递增,结果 s1 变为 [2, 3, 1]。附加操作还更改了底层数组,因此 s2 现在看到 [2, 3, 1]incr(s2),s2 被追加 0,所有项递增,结果 s2 变为 [3, 4, 2, 1]。增量也影响了底层数组,所以现在 s1 看到 [3, 4, 2]显然我对 Go 中 slice 的理解存在巨大错误。请告诉我哪里错了。看来我的推理与切片的行为是一致的。(我知道附加到容量不足的切片也会重新分配底层数组,但不知道如何将其放入其中)。
我们来逐步分析一下这个程序:
s1 := []int{1, 2} // s1 -> [1,2]
s2 := s1 // s1, s2 -> [1,2]
Run Code Online (Sandbox Code Playgroud)
接下来的操作是:
s2 = append(s2, 3)
Run Code Online (Sandbox Code Playgroud)
如果底层数组的容量不够,这可能会分配一个新的后备数组。在这种情况下,它将给出:
s1 -> [1,2]
s2 -> [1,2,3]
Run Code Online (Sandbox Code Playgroud)
然后incr(s1)将向 追加一个新元素s1,递增值,但生成的切片不会分配给s1main 中,所以仍然:
s1 -> [1,2]
s2 -> [1,2,3]
Run Code Online (Sandbox Code Playgroud)
incr(s2)会做同样的事情,但这一次,后备数组有能力保存附加的零,因此增量操作会递增值,但新切片永远不会分配给s2in main。所以,s2仍然有3个要素:
s1 -> [1,2]
s2 -> [2,3,4]
Run Code Online (Sandbox Code Playgroud)
的支持数组s2中还有一个元素,但s2不包含该元素。
| 归档时间: |
|
| 查看次数: |
503 次 |
| 最近记录: |