英文:
behavior of map's value pointer being added to slice
问题
func TestMapValuePointer2(t *testing.T) {
fmt.Println("Test Map Value Pointer 2")
m := map[string]int{"rsc": 3711, "r": 2138, "gri": 1908, "adg": 912}
n := len(m)
array := make([]*int, n)
i := 0
for _, v := range m {
array[i] = &v
fmt.Printf("Add to array: %d\n", v)
i++
}
for _, k := range array {
fmt.Printf("Value: %d\n", *k)
}
}
输出结果不是:
Value: 912
Value: 3711
Value: 2138
Value: 1908
而是可能像这样:
Value: 912
Value: 912
Value: 912
Value: 912
有人能解释为什么结果会是上面那样吗?
英文:
func TestMapValuePointer2(t *testing.T) {
fmt.Println("Test Map Value Pointer 2")
m := map[string]int{"rsc": 3711, "r": 2138, "gri": 1908, "adg": 912}
n := len(m)
array := make([]*int, n)
i := 0
for _, v := range m {
array[i] = &v
fmt.Printf("Add to array: %d\n", v)
i++
}
for _, k := range array {
fmt.Printf("Value: %d\n", *k)
}
}
The output is not:
<pre>
Value: 912
Value: 3711
Value: 2138
Value: 1908
</pre>
Instead, the output maybe like:
<pre>
Value: 912
Value: 912
Value: 912
Value: 912
</pre>
Can someone explain why the results are like the above?
答案1
得分: 2
引用自这个文档:
> [...] 循环的每次迭代都使用同一个变量v的实例,因此每个闭包共享该单个变量 [...]
换句话说,循环变量v
在所有迭代中被重用,因此您将相同的指针分配给了切片的所有元素。
在循环内部声明的变量不会像那样被循环利用,所以这样做会按您的预期工作:
for _, v := range m {
vv := v
array[i] = &vv
fmt.Printf("Add to array: %d\n", v)
i++
}
顺便说一下,您还没有解释为什么要将*int
作为值的类型。如果您只使用简单的int
值,所有这些都会更简单。
英文:
Quoting from this doc:
> [...] each iteration of the loop uses the same instance of the variable v, so each closure shares that single variable [...]
In other words, the loop variable v
is reused in all iterations, so your assigning the same pointer to all your slice elements.
Variables declared inside the loop won't be recycled like that, so this will work as you would expect:
for _, v := range m {
vv := v
array[i] = &vv
fmt.Printf("Add to array: %d\n", v)
i++
}
Btw, you haven't explained why you want to use *int
as the type of the values. All this would be simpler if you used simply int
values.
答案2
得分: 1
这里的问题是,在循环期间创建的变量v
实际上是映射中的值的副本。与v
相关的内存在循环开始时分配一次,并在后面用于其他值,因此最后一个值指向的是912,在数组中你看到的是912。
一个简单的证明是:https://play.golang.org/p/K0yAbEIf3G
修复这个问题的方法是将映射的值更改为*int指针,而不是值,然后我们可以解引用它们以获取实际的值:
package main
import "fmt"
func main() {
fmt.Println("Test Map Value Pointer 2")
a, b, c, d := 3711, 2138, 1908, 912
m := map[string]*int{"rsc": &a, "r": &b, "gri": &c, "adg": &d}
n := len(m)
array := make([]*int, n)
i := 0
for _, v := range m {
array[i] = v
fmt.Printf("Add to array: %d\n", *v)
i++
}
for _, k := range array {
fmt.Printf("Value: %d\n", *k)
}
}
Playground链接:https://play.golang.org/p/TpkXlCElLx
英文:
The issue here is that the variable v
created during the loop is actually a copy of the value we have in the map. The memory related to v
is allocated once at the start of the loop and is re-used later on for other values, as the last value here points to is 912 in this case you're seeing 912 inside the array.
A simple proof for this will be: https://play.golang.org/p/K0yAbEIf3G
One fix for this will be to change your map value to be *int pointers instead of values and later on we can dereference them to get the actual values:
package main
import "fmt"
func main() {
fmt.Println("Test Map Value Pointer 2")
a, b, c, d := 3711, 2138, 1908, 912
m := map[string]*int{"rsc": &a, "r": &b, "gri": &c, "adg": &d}
n := len(m)
array := make([]*int, n)
i := 0
for _, v := range m {
array[i] = v
fmt.Printf("Add to array: %d\n", *v)
i++
}
for _, k := range array {
fmt.Printf("Value: %d\n", *k)
}
}
Playground link: https://play.golang.org/p/TpkXlCElLx
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论