【问题标题】:Golang Reusing Memory Address Copying from slice?Golang重用从切片复制内存地址?
【发布时间】:2015-04-28 15:45:59
【问题描述】:

我正在处理的项目中遇到问题。我找到了解决方法,但我不确定为什么我的解决方案有效。我希望有人对 Go 指针的工作方式有更多经验可以帮助我。

我有一个 Model 接口和一个实现该接口的 Region 结构。 Model 接口是在 Region 结构的指针上实现的。我还有一个 Regions 集合,它是 Region 对象的一部分。我有一个方法可以将 Regions 对象转换为 []Model:

// Regions is the collection of the Region model
type Regions []Region

// Returns the model collection as a list of models
func (coll *Regions) ToModelList() []Model {
    output := make([]Model, len(*coll))
    for idx, item := range *coll {
        output[idx] = &item
    }
    return output
}

当我运行此代码时,我最终会多次输出指向区域的第一个指针。所以,如果 Regions 集合有两个不同的项目,我会得到相同的地址重复两次。当我在切片中设置变量之前打印变量时,它们具有正确的数据。

我有点搞砸了,认为 Go 可能会在循环之间重用内存地址。此解决方案目前在我的测试中为我工作:

// Returns the model collection as a list of models
func (coll *Regions) ToModelList() []Model {
    output := make([]Model, len(*coll))
    for idx, _ := range *coll {
        i := (*coll)[idx]
        output[idx] = &i
    }
    return output
}

这给出了输出切片中两个不同地址的预期输出。

老实说,这似乎是 range 函数在运行之间重用相同内存地址的错误,但我总是认为在这种情况下我会遗漏一些东西。

我希望我已经为您解释得足够好。我很惊讶原来的解决方案不起作用。

谢谢!

【问题讨论】:

  • 旁注,您几乎从不想使用指向切片的指针(即您的coll *Regions,实际上是*[]Region)。 Slices 很小并且已经包含指向数据的指针。唯一需要为 Regions 使用指针接收器的情况是您需要更改方法中的长度和/或容量。

标签: pointers go


【解决方案1】:

在您的第一个(非工作)示例中,item 是循环变量。它的地址不变,只有它的值。这就是为什么您在输出 idx 次中得到相同地址的原因。

运行此代码以查看运行中的机制;

func main() {

    coll := []int{5, 10, 15}

    for i, v := range coll {
       fmt.Printf("This one is always the same; %v\n", &v)
       fmt.Println("This one is 4 bytes larger each iteration; %v\n", &coll[i])
    }
}

【讨论】:

  • 我的印象是,由于我在列表中存储了对指针的引用,因此 Go 不应该重用该内存地址。我这样想错了吗?
  • @jnadro52 给我一些代码示例。问题是您正在获取项目的地址,它只是循环变量。您实际上想要分配它的值,因为 item 本身已经是一个指针。
  • Go 中没有“引用”的概念。没有,并且在参考方面的思考会导致错误。
  • @evanmcdonnal - 这是有道理的。我取指针的原因是指针接收器实现了模型接口,所以我必须实际使用指针,否则编译器会骂我。
  • @Volker - 我认为我必须对 GC 的工作方式以及 Go 如何使用这些内存地址进行更多研究。我假设您的意思是它只是将指针存储为内存地址,但不将它们视为引用的内存。你有什么好的链接来解释它吗?
【解决方案2】:

整个循环只有一个item变量,在循环的每次迭代中都被赋予了相应的值。您不会在每次迭代中获得新的 item 变量。所以你只是重复取同一个变量的地址,当然是一样的。

另一方面,如果你在循环中声明了一个局部变量,那么每次迭代都会是一个新的变量,地址也会不同:

for idx, item := range *coll {
    temp := item
    output[idx] = &temp
}

【讨论】:

  • 这是一个非常简洁易懂的描述,谢谢。值得注意的是,创建一个新变量并将其分配给指向该项目的指针也不起作用。我也试过了:-)
猜你喜欢
  • 2019-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多