【问题标题】:behavior of map's value pointer being added to slice将映射的值指针添加到切片的行为
【发布时间】:2017-04-05 11:16:23
【问题描述】:
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)
    }
}

输出不是:

价值:912 价值:3711 价值:2138 价值:1908

相反,输出可能是:

价值:912 价值:912 价值:912 价值:912

有人能解释一下为什么结果会像上面那样吗?

【问题讨论】:

标签: pointers dictionary go slice


【解决方案1】:

引用this doc:

[...] 循环的每次迭代都使用变量 v 的相同实例,因此每个闭包共享该单个变量 [...]

换句话说,循环变量v 在所有迭代中都被重用,因此您将相同的指针分配给所有切片元素。

inside 声明的变量循环不会像那样被回收,所以这会像你期望的那样工作:

for _, v := range m {
    vv := v
    array[i] = &vv
    fmt.Printf("Add to array: %d\n", v)
    i++
}

顺便说一句,您还没有解释为什么要使用 *int 作为值的类型。如果您只使用 int 值,这一切都会更简单。

【讨论】:

  • 我不确定这里的 OP 期望值是什么,但是这样做,数组和映射中的指针现在将遵循不同的位置。
【解决方案2】:

这里的问题是在循环期间创建的变量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)
    }
}

游乐场链接:https://play.golang.org/p/TpkXlCElLx

【讨论】:

    猜你喜欢
    • 2015-10-16
    • 2015-11-09
    • 1970-01-01
    • 2017-08-11
    • 2016-10-16
    • 2011-09-26
    • 2021-11-26
    • 2021-09-01
    • 2013-02-11
    相关资源
    最近更新 更多