map的值指针被添加到切片的行为

huangapple go评论68阅读模式
英文:

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] = &amp;vv
    fmt.Printf(&quot;Add to array: %d\n&quot;, 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] = &amp;vv
    fmt.Printf(&quot;Add to array: %d\n&quot;, 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 &quot;fmt&quot;

func main() {
    fmt.Println(&quot;Test Map Value Pointer 2&quot;)
    a, b, c, d := 3711, 2138, 1908, 912
    m := map[string]*int{&quot;rsc&quot;: &amp;a, &quot;r&quot;: &amp;b, &quot;gri&quot;: &amp;c, &quot;adg&quot;: &amp;d}
    n := len(m)
    array := make([]*int, n)
    i := 0
    for _, v := range m {
        array[i] = v
        fmt.Printf(&quot;Add to array: %d\n&quot;, *v)
        i++
    }
    for _, k := range array {
        fmt.Printf(&quot;Value: %d\n&quot;, *k)
    }
}

Playground link: https://play.golang.org/p/TpkXlCElLx

huangapple
  • 本文由 发表于 2016年11月22日 06:39:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/40730701.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定