golang中的那些坑之迭代器中的指针使用
今天在编写代码的时候,遇到了一个莫名其妙的错误,debug了半天,发现这是一个非常典型且易犯的错误.记之
示例代码:
package main import "fmt" type aa struct { x, y int } type bb struct { member aa } func main() { m := []*aa{} pool := []bb { { member: aa{x : 1,y : 1,}, }, { member: aa{x : 2,y : 2,}, }, { member: aa{x : 3,y : 3,}, }, } for _, p := range pool { m = append(m, &p.member) } for _, ele := range m { ele.x = ele.x + 1 ele.y = ele.y + 1 } for _, ele := range m { fmt.Printf("x=%d, y=%d\n", ele.x, ele.y) } }
上面这段代码的运行结果是什么?也许你会立马答出来是:
x=2, y=2
x=3, y=3
x=4, y=4
运行一下就知道这个答案是错的.正确的运行结果是:
x=6, y=6
x=6, y=6
x=6, y=6
那么到底错在哪呢?原来在数组m中,它的三个元素是同一个指针.原来在下面这段代码中,golang是新建了一个变量p, 每次将该数组的元素
赋值给这个p,而p的地址自然是恒定的,因此最后m中的元素都是这个p的成员变量member的地址.
for _, p := range pool { m = append(m, &p.member) }
这个例子告诉我们,在golang的数组/map迭代操作中,如果在迭代体中需要访问数组/map元素的指针,那么千万要小心了.这类bug非常难以检测.