for-range

下面这段代码输出什么?

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	//相当于这里在循环之前,隐式先将a数组拷贝到b里
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中指向的原数组取出修改后的值。


for-range
http://example.com/2023/04/19/Go每日一题/for-range/
作者
Feng Tao
发布于
2023年4月19日
更新于
2023年4月21日
许可协议