下面这段代码输出什么?
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| func main() { var a = [5]int{1, 2, 3, 4, 5} var r [5]int
for i, v := range a { if i == 0 { a[1] = 12 a[2] = 13 } r[i] = v } fmt.Println("r = ", r) fmt.Println("a = ", a) }
|
答案
1 2
| r = [1 2 3 4 5] a = [1 12 13 4 5]
|
range 表达式是副本参与循环,即对于range x
,我们复制了x,这里假设将其拷贝到b,在for循环里执行的时候,每次遍历的就是x的副本b,在例子里,就是每次遍历的是a的副本b,而不是a本身,则实际的range循环代码是下面这样的:
1 2 3 4 5 6 7 8
| var b = a for i, v := range b { if i == 0 { a[1] = 12 a[2] = 13 } r[i] = v }
|
因此无论 a 被如何修改,其副本 b 依旧保持原值,并且参与循环的是 b,因此 v 从 b 中取出的仍旧是 a 的原值,而非修改后的值。
如果想要 r 和 a 一样输出,修改如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| func main() { var a = [5]int{1, 2, 3, 4, 5} var r [5]int
for i, v := range &a { if i == 0 { a[1] = 12 a[2] = 13 } r[i] = v } fmt.Println("r = ", r) fmt.Println("a = ", a) }
|
输出:
1 2
| r = [1 12 13 4 5] a = [1 12 13 4 5]
|
修改后的代码是将地址传入了range表达式,其副本依旧是一个指向原数组a的指针,因此后续所有循环中均是&a指向的原数组参与的,故v能从&a中指向的原数组取出修改后的值。