为了账号安全,请及时绑定邮箱和手机立即绑定

关于映射、字符串指针和闭包的问题

关于映射、字符串指针和闭包的问题

Go
蓝山帝景 2022-06-13 15:03:41
当我尝试转换为时,我发现我的代码存在错误(我map[string]string正在map[string]*string使用的 API 需要转换)。问题在我的第一个方法中,我尝试遍历源映射中的每个条目,aMap并将每个条目的字符串值转换为字符串指针,并将此指针值分配给目标映射,bMap与aMap. 我最初预计保留的取消引用值bMap与aMap同一键下的值相同。但是,事实证明,在同一键下, 的延迟值bMap不共享相同的值。aMap我的问题是:为什么会出现这个问题?我能够使用第二种方法解决该问题,首先定义一个接收字符串并返回指针的函数,这样我就能够正确返回延迟值以共享与aMap同一键下相同的值。为什么这种方法有效?另外,只是好奇,为什么&(*v)Go 中有语法错误?我还尝试声明一个虚拟变量val := *v,并将其分配给bMapusingbMap[key] = &val但无济于事。错误说invalid indirect of v (type string)源代码https://play.golang.org/p/ZG7XS2vJx0yfunc main() {    aMap := make(map[string]string)    aMap["foo"] = "bar"    aMap["bar"] = "baz"    aMap["baz"] = "foo"    bMap := make(map[string]*string)    for k, v := range(aMap){        bMap[k] = &v    }    // first method    fmt.Println("1st method, map[string]*string, bMap")     for k, v := range(bMap){        fmt.Printf("bMap[%s] = %s / %s, aMap[%s] = %s\n",        k, *bMap[k], *v, k, aMap[k])    }    pString := func(v string) *string{ return &v }    for k, v := range(aMap){        bMap[k] = pString(v)    }    // second method    fmt.Println("2nd method, map[string]*string, bMap")    for k, v := range(bMap){        fmt.Printf("bMap[%s] = %s / %s, aMap[%s] = %s\n",        k, *bMap[k], *v, k, aMap[k])    }    // expected results    fmt.Println("Expected result: map[string]string, cMap")    cMap := make(map[string]string)    for k, v := range(aMap){        cMap[k] = v    }    for k, v := range(cMap){        fmt.Printf("cMap[%s] = %s / %s, aMap[%s] = %s\n",        k, cMap[k], v, k, aMap[k])    }}输出1st method, map[string]*string, bMapbMap[baz] = foo / foo, aMap[baz] = foobMap[foo] = foo / foo, aMap[foo] = barbMap[bar] = foo / foo, aMap[bar] = baz2nd method, map[string]*string, bMapbMap[baz] = foo / foo, aMap[baz] = foobMap[foo] = bar / bar, aMap[foo] = barbMap[bar] = baz / baz, aMap[bar] = bazExpected result: map[string]string, cMapcMap[baz] = foo / foo, aMap[baz] = foocMap[foo] = bar / bar, aMap[foo] = barcMap[bar] = baz / baz, aMap[bar] = baz太感谢了。
查看完整描述

2 回答

?
牧羊人nacy

TA贡献1862条经验 获得超7个赞

方法 1 中的问题是您没有获取字符串的地址,而是获取了它所在变量的地址。在 Go 中,&v返回变量的地址v。当你有这样的循环时:


for k, v := range aMap {

    ...

}

您在循环开始时声明的变量k和v在整个循环中使用的变量是相同的。它们只是在每次迭代中被分配了不同的值。在该循环中,&v总是计算出相同的值: 的地址v。这就是为什么您的所有地图条目都出现在"foo":"foo"中的最后一个值v,并且它仍然存在。


您可以通过更改字符串值来查看此行为。所有的地图键都会改变:


*bMap["foo"] = "quux"

fmt.Println(*bMap["bar"]) // prints "quux"

方法 2 有效,因为函数的每次调用都有自己的局部变量。Go 保证,如果您从函数返回局部变量的地址,该变量将被分配在堆上,只要需要它就可以使用它。所以你的辅助函数告诉 Go 分配一个新string变量,将传入的字符串复制到它,然后返回它的地址。


这是另一种可行的方法:


dMap := make(map[string]*string)

for k, v := range(aMap){

    dMap[k] = new(string)

    *dMap[k] = v

}

这会分配一个新的字符串变量,将其地址保存在映射中,然后将 v 复制到其中。如果您要经常这样做,辅助函数可能是最好的。


您尝试的代码如下:


v := "foo"

val := *v

bMap[key] = &val

不起作用,因为您说“v 是一个字符串;现在将字符串指向的值存储在 val 中”,但该字符串不是指针。


查看完整回答
反对 回复 2022-06-13
?
哔哔one

TA贡献1854条经验 获得超8个赞

问题中的第一种方法使用v所有键的单个循环变量的地址。您看到的值是最后一个设置为 的值v。


通过为每次迭代声明一个新变量并获取该变量的地址来修复。


bMap := make(map[string]*string)

for k, v := range aMap {

    v := v // declare new variable v initialized with value from outer v

    bMap[k] = &v

}

问题中的第二种方法还为循环中的每次迭代声明了一个新变量。新变量是函数参数。


这个答案显示了解决这个问题的惯用方法。请参阅Go 常见问题解答:作为 goroutine 运行的闭包会发生什么?在闭包和 goroutine 的上下文中讨论相同的问题。


查看完整回答
反对 回复 2022-06-13
  • 2 回答
  • 0 关注
  • 186 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号