Golang重用内存地址从切片复制?

时间:2015-04-28 15:45:59

标签: pointers go

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

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

// 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
}

当我运行这段代码时,我最终得到了多次输出Region的第一个指针。因此,如果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
}

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

老实说这似乎是范围函数在运行之间重用相同内存地址的错误,但我总是假设我在这种情况下缺少某些东西。

我希望我能为你解释得这么好。我很惊讶原始解决方案不起作用。

谢谢!

2 个答案:

答案 0 :(得分:6)

在您的第一个(非工作)示例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])
    }
}

答案 1 :(得分:2)

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

另一方面,如果你在循环中声明了一个局部变量,它将在每次迭代中成为一个新变量,并且地址将是不同的:

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