golang中for range的坑点

golang中for range的坑点

GOLANG 可通过 for range 遍历 数组、切片、映射等类型。

但使用不当时 有时程序运行会不符合预期。

错误例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

func main() {
sourceArr := []int{0, 1, 2, 3}
targetArr := []*int{}

for _, num := range sourceArr {
targetArr = append(targetArr, &num)
}

for _, num_p := range targetArr {
fmt.Printf("num: %v\n", *num_p)
}
}
1
2
3
4
5
6
7
8
9
10
11
# 预期结果为
num: 0
num: 1
num: 2
num: 3

# 实际结果为
num: 3
num: 3
num: 3
num: 3

原因分析

进一步分析打印程序如下 打印targetArr如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

func main() {
sourceArr := []int{0, 1, 2, 3}
targetArr := []*int{}

for _, num := range sourceArr {
fmt.Printf("&num: %+v\n", &num)
targetArr = append(targetArr, &num)
}

for _, num_p := range targetArr {
fmt.Printf("num: %v\n", *num_p)
}
fmt.Printf("targetArr: %v\n", targetArr)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 结果如下
&num: 0xc00018c000
&num: 0xc00018c000
&num: 0xc00018c000
&num: 0xc00018c000
num: 3
num: 3
num: 3
num: 3
targetArr: [0xc00018c000 0xc00018c000 0xc00018c000 0xc00018c000]

可见 range 的第二返回值 始终指向同一个地址
导致 *num_p 取得的都是 sourceArr 的最后一项

!!! 一旦在for range中 使用了 第二返回值 & 取值赋值时,都要谨慎处理

正确处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"

func main() {
sourceArr := []int{0, 1, 2, 3}
targetArr := []*int{}

for idx := range sourceArr {
targetArr = append(targetArr, &sourceArr[idx])
}

for _, num_p := range targetArr {
fmt.Printf("num: %v\n", *num_p)
}
}

当存在指针传递时,尽量使用index作为赋值依据,上述程序 符合预期


golang中for range的坑点
https://yaocc.cc/golangforrangetrap/
作者
Yaocan Chen
发布于
2021年10月15日
许可协议