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

huangapple go评论89阅读模式
英文:

Golang Reusing Memory Address Copying from slice?

问题

我在我正在进行的一个项目中遇到了一个问题。我找到了一个解决方法,但我不确定为什么我的解决方案有效。我希望有更多了解Go指针工作原理的人能够帮助我。

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

// Regions是Region模型的集合
type Regions []Region

// 将模型集合转换为模型列表
func (coll *Regions) ToModelList() []Model {
output := make([]Model, len(*coll))
for idx, item := range *coll {
output[idx] = &item
}
return output
}

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

我稍微调整了一下,以为Go可能会在循环之间重用内存地址。这个解决方案在我的测试中目前有效:

// 将模型集合转换为模型列表
func (coll *Regions) ToModelList() []Model {
output := make([]Model, len(*coll))
for idx, _ := range *coll {
i := (*coll)[idx]
output[idx] = &i
}
return output
}

这样就可以在输出切片中得到两个不同的地址。

老实说,这似乎是range函数在运行之间重用相同内存地址的一个bug,但在这种情况下,我总是假设我可能遗漏了一些东西。

希望我解释得足够清楚。我对原始解决方案不起作用感到惊讶。

谢谢!

英文:

I was hitting an issue in a project I'm working on. I found a way around it, but I wasn't sure why my solution worked. I'm hoping that someone more experience with how Go pointers work could help me.

I have a Model interface and a Region struct that implements the interface. The Model interface is implemented on the pointer of the Region struct. I also have a Regions collection which is a slice of Region objects. I have a method that can turn a Regions object into a []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
}

When I run this code, I end up with the first pointer to the Region outputted multiple times. So, if the Regions collection has two distinct items, I will get the same address duplicated twice. When I print the variables before I set them in the slice, they have the proper data.

I messed with it a little bit, thinking Go might be reusing the memory address between loops. This solution is currently working for me in my tests:

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

This gives the expected output of two distinct addresses in the output slice.

This honestly seems like a bug with the range function reusing the same memory address between runs, but I always assume I'm missing something in cases like this.

I hope I explained this well enough for you. I'm surprised that the original solution did not work.

Thanks!

答案1

得分: 11

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

运行以下代码以查看其工作原理:

func main() {
    
    coll := []int{5, 10, 15}

    for i, v := range coll {
       fmt.Printf("这个始终是相同的:%v\n", &v)
       fmt.Printf("这个每次迭代都比前一个大 4 个字节:%v\n", &coll[i])
    }
}

请注意,这只是一个示例代码,用于演示变量地址的变化。实际上,在循环中获取变量的地址可能不是一个常见的需求。

英文:

In your first (non working) example item is the loop variable. Its address is not changing, only its value. That's why you get the same address in output idx times.

Run this code to see the mechanics in action;

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])
    }
}

答案2

得分: 2

在整个循环中,只有一个item变量,它在每次迭代中被赋予相应的值。在每次迭代中,你不会得到一个新的item变量。因此,你只是重复地获取同一个变量的地址,这当然是相同的。

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

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

There is just one item variable for the entire loop, which is assigned the corresponding value during each iteration of the loop. You do not get a new item variable in each iteration. So you are just repeatedly taking the address of the same variable, which will of course be the same.

On the other hand, if you declared a local variable inside the loop, it will be a new variable in each iteration, and the addresses will be different:

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

huangapple
  • 本文由 发表于 2015年4月28日 23:45:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/29923956.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定