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

时间:2016-11-21 22:39:17

标签: pointers dictionary go 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

有人可以解释为什么结果如上所述吗?

2 个答案:

答案 0 :(得分:2)

引自this doc

  

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

换句话说,循环变量// A C implementation of memcpy() #include<stdio.h> #include<string.h> void myMemCpy(void *dest, void *src, size_t n) { // Typecast src and dest addresses to (char *) char *csrc = (char *)src; char *cdest = (char *)dest; // Copy contents of src[] to dest[] for (int i=0; i<n; i++) cdest[i] = csrc[i]; } 在所有迭代中都会被重用,因此您可以为所有切片元素指定相同的指针。

在循环中声明循环的变量不会像那样被回收,所以这将按预期工作:

v

顺便说一句,您还没有解释为什么要使用for _, v := range m { vv := v array[i] = &vv fmt.Printf("Add to array: %d\n", v) i++ } 作为值的类型。如果您只使用*int值,那么所有这些都会更简单。

答案 1 :(得分: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)
    }
}

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