Pointers 如何将指针指定给Go地图

Pointers 如何将指针指定给Go地图,pointers,dictionary,go,Pointers,Dictionary,Go,我在为地图分配指针时遇到问题。也许这是围棋中的一个错误?也许我只是做错了什么。代码也在操场上 下面是一些超级简化的代码来说明这个问题。我正在创建一个名为collections的对象,其中包含两个collection对象。然后我循环遍历这些集合,并将它们分配到一个映射,映射中的键是集合ID package main import ( "fmt" ) type collection struct { ID string Name string } type collec

我在为地图分配指针时遇到问题。也许这是围棋中的一个错误?也许我只是做错了什么。代码也在操场上

下面是一些超级简化的代码来说明这个问题。我正在创建一个名为collections的对象,其中包含两个collection对象。然后我循环遍历这些集合,并将它们分配到一个映射,映射中的键是集合ID

package main

import (
    "fmt"
)

type collection struct {
    ID string
    Name string
}

type collections struct {
    Collections []collection
}

type cache struct {
    Index int
    Collections map[string]*collection
}

func main() {
    var c cache
    c.Collections = make(map[string]*collection)

    // Create 2 Collections
    var col1, col2 collection
    col1.ID = "aa"
    col1.Name = "Text A"
    col2.ID = "bb"
    col2.Name = "Test B"

    // Add to Collections
    var cols collections
    cols.Collections = append(cols.Collections, col1)
    cols.Collections = append(cols.Collections, col2)
    fmt.Println("DEBUG Collections Type", cols)

    i := 0
    for k, v := range cols.Collections {
        c.Index = i
        c.Collections[v.ID] = &v
        fmt.Println("DEBUG k", k)
        fmt.Println("DEBUG v", v)
        i++
    }
    fmt.Println("Collection 1", c.Collections["aa"].ID)
    fmt.Println("Collection 2", c.Collections["bb"].ID)
    fmt.Println(c)
}
此代码的输出如下所示:

DEBUG Collections Type {[{aa Text A} {bb Test B}]}
DEBUG k 0
DEBUG v {aa Text A}
DEBUG k 1
DEBUG v {bb Test B}
Collection 1 bb
Collection 2 bb
{1 map[aa:0x1040a0f0 bb:0x1040a0f0]}

因此,似乎出于某种原因,映射为每个条目获取相同的指针。所有的“调试”行都打印出我所期望的内容。但是,最末端的三条打印线不可用。集合1应该是“aa”而不是“bb”。

当您将
&v
放入
c.Collections[v.ID]
时,您实际上是在分配循环变量
v
的相同地址

此地址最终保存列表的最后一个值。这就是为什么您要为所有密钥获取
bb测试B

打印这些值,您将看到相同的地址

fmt.Printf("%p\n", c.Collections["aa"])
fmt.Printf("%p\n", c.Collections["bb"])
通过将其复制到循环范围中的一个新变量,问题得到了解决。循环中的每一步都会将一个新的唯一地址放入缓存

for k, v := range cols.Collections {
    coll := v
    c.Collections[v.ID] = &coll
    fmt.Println("DEBUG k", k)
    fmt.Println("DEBUG v", v)
    i++
}

关于同一指针的答案是,映射包含变量v的地址,但不包含数组项

for k, v := range cols.Collections {
   ......
   c.Collections[v.ID] = &v
正确的解决方案是存储指向数组的指针,而不是值

type collections struct {
     Collections []*collection
}

cols.Collections = append(cols.Collections, &col1)
cols.Collections = append(cols.Collections, &col2)
或获取地图项目的地址(作为更高的答案)


不幸的是,第一个答案中的代码有一个错误-指向局部变量coll的指针:

for k, v := range cols.Collections {
    coll := v
    c.Collections[v.ID] = &coll
只需尝试更改集合的属性值:

cols.Collections[0].Name = "Text C"
fmt.Println("Collection 1", cols.Collections[0].Name)
fmt.Println("Collection 1", c.Collections["aa"].Name)
//  Collection 1 Text C
//  Collection 1 Text A
但另一个代码将打印预期结果:

for k, v := range cols.Collections {
    c.Index = i
    p := &cols.Collections[k]
    c.Collections[v.ID] = p

....
cols.Collections[0].Name = "Text C"
fmt.Println("Collection 1", cols.Collections[0].Name)
fmt.Println("Collection 1", c.Collections["aa"].Name)
// Collection 1 Text C
// Collection 1 Text C

不需要复制id,建议在Go中使用下划线作为变量。我想你们应该提到为什么要这样做。在围棋中,把地址带到一个局部变量是很好的。Go编译器通过让变量“转义”到堆来处理这个问题。获取局部变量的地址是正常的。但这通常是错误(闭包、多个集合等)的原因,尤其是以字节片作为属性的意外suprice。其次,使用两个或多个对象的副本会导致内存和性能的大量使用。对于这个例子,IMHO,两个或多个集合作为索引的情况下有一个错误。您的解决方案是另一个用例场景。如果OP想要分离
切片
映射
,该怎么办。你可以解释利弊。但是说我的解决方案有错误是不明智的。获取复制值的地址和存储在切片中的地址之间存在差异:如果切片值发生变化,映射的值会发生变化吗?虽然OP没有指定,但我认为让两个操作系统彼此独立是更有意义的。操作系统使用的问题。存储相同对象的地址对于两个集合作为索引访问一个集合来说很重要。
for k, v := range cols.Collections {
    c.Index = i
    p := &cols.Collections[k]
    c.Collections[v.ID] = p

....
cols.Collections[0].Name = "Text C"
fmt.Println("Collection 1", cols.Collections[0].Name)
fmt.Println("Collection 1", c.Collections["aa"].Name)
// Collection 1 Text C
// Collection 1 Text C