附加到具有相同底层数组的两个切片,为什么结果?
Appending to two slice with same underlying array, why the result?
这是一些 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]"
}
我不明白为什么结果是“[1, 2][2, 3, 4]”。
基于 https://golang.org/doc/effective_go#slices 的两点:
Slices hold references to an underlying array, and if you assign one slice to another, both refer to the same array
If a function takes a slice argument, changes it makes to the elements of the slice will be visible to the caller, analogous to passing a pointer to the underlying array
这是我想象中应该发生的事情:
- 起初,s1 和 s2 都有相同的底层数组 [1, 2]
- 将 3 添加到 s2 后,底层数组变为 [1, 2, 3]。但是 s1 仍然只看到 [1, 2]
- 在
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]
- 所以打印出来的结果应该是[3, 4, 2][3, 4, 2, 1]
我显然在理解 Go 中的 slice 时犯了很大的错误。请告诉我哪里错了。看来我的推理与 slice 的行为是一致的。 (我知道附加到容量不足的切片也会重新分配一个底层数组,但不知道如何将其放入其中)。
让我们一步步分析这个程序:
s1 := []int{1, 2} // s1 -> [1,2]
s2 := s1 // s1, s2 -> [1,2]
接下来的操作是:
s2 = append(s2, 3)
如果基础数组的容量不足,这可能分配一个新的支持数组。在这种情况下,它会这样做:
s1 -> [1,2]
s2 -> [1,2,3]
然后incr(s1)
会追加一个新元素到s1
,增加值,但是得到的slice不会赋值给main中的s1
,所以还是:
s1 -> [1,2]
s2 -> [1,2,3]
incr(s2)
会做同样的事情,但是这次,后备数组有能力保存附加的零,所以增量操作会增加值,但是新的切片永远不会分配给 s2
在 main
中。所以,s2
仍然有 3 个元素:
s1 -> [1,2]
s2 -> [2,3,4]
s2
的后备数组中还有一个元素,但 s2
不包含该元素。
这是一些 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]"
}
我不明白为什么结果是“[1, 2][2, 3, 4]”。
基于 https://golang.org/doc/effective_go#slices 的两点:
Slices hold references to an underlying array, and if you assign one slice to another, both refer to the same array
If a function takes a slice argument, changes it makes to the elements of the slice will be visible to the caller, analogous to passing a pointer to the underlying array
这是我想象中应该发生的事情:
- 起初,s1 和 s2 都有相同的底层数组 [1, 2]
- 将 3 添加到 s2 后,底层数组变为 [1, 2, 3]。但是 s1 仍然只看到 [1, 2]
- 在
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] - 所以打印出来的结果应该是[3, 4, 2][3, 4, 2, 1]
我显然在理解 Go 中的 slice 时犯了很大的错误。请告诉我哪里错了。看来我的推理与 slice 的行为是一致的。 (我知道附加到容量不足的切片也会重新分配一个底层数组,但不知道如何将其放入其中)。
让我们一步步分析这个程序:
s1 := []int{1, 2} // s1 -> [1,2]
s2 := s1 // s1, s2 -> [1,2]
接下来的操作是:
s2 = append(s2, 3)
如果基础数组的容量不足,这可能分配一个新的支持数组。在这种情况下,它会这样做:
s1 -> [1,2]
s2 -> [1,2,3]
然后incr(s1)
会追加一个新元素到s1
,增加值,但是得到的slice不会赋值给main中的s1
,所以还是:
s1 -> [1,2]
s2 -> [1,2,3]
incr(s2)
会做同样的事情,但是这次,后备数组有能力保存附加的零,所以增量操作会增加值,但是新的切片永远不会分配给 s2
在 main
中。所以,s2
仍然有 3 个元素:
s1 -> [1,2]
s2 -> [2,3,4]
s2
的后备数组中还有一个元素,但 s2
不包含该元素。