For loop 在Go中复制循环变量的地址
在下面的代码示例中,结果不是我所期望的:For loop 在Go中复制循环变量的地址,for-loop,go,reference,For Loop,Go,Reference,在下面的代码示例中,结果不是我所期望的: package main import "fmt" func main() { src := map[int]int{1: 1, 2: 2, 3: 3} fmt.Println("src ", src) dst := make([]*int, 0, len(src)) for k, _ := range src { dst = append(dst, &k) } for _,
package main
import "fmt"
func main() {
src := map[int]int{1: 1, 2: 2, 3: 3}
fmt.Println("src ", src)
dst := make([]*int, 0, len(src))
for k, _ := range src {
dst = append(dst, &k)
}
for _, a := range dst {
fmt.Print(*a, " ")
}
fmt.Println()
}
结果:
src map[1:1 2:2 3:3]
3 3 3
前往游乐场:
但我明白发生了什么。k
的不变地址被添加到dst
,因此当我在dst
上循环时,每个位置都有相同的值:3
k
的地址在循环中从未更改,因此第二个循环一直引用该位置,其中包含它拥有的最后一个值3
如何获取要复制的k
当前值的地址?我需要这样的东西吗:
for k, _ := range src {
key = new(int)
*key = k
dst = append(dst, key)
}
这似乎很尴尬。如果您有一个
映射[T]X
,并且您想要得到一个[]*T
,那么您复制循环变量并获取其地址的方法是正确的
有一种方法比你的方法更简单:
for k := range src {
key := k
dst = append(dst, &key)
}
您添加到dst的不是映射中键或值的地址,而是键的副本的地址。这种区别对你来说可能重要,也可能不重要
使用循环变量的地址不起作用的原因是,循环变量是在每次迭代中更新的单个位置。每次将INT和STRUCT之类的值类型分配给新变量时都会被复制。如果您有一个
映射[T]X
,并且希望获得一个[]*T
,那么您复制循环变量并获取其地址的方法是正确的
有一种方法比你的方法更简单:
for k := range src {
key := k
dst = append(dst, &key)
}
您添加到dst的不是映射中键或值的地址,而是键的副本的地址。这种区别对你来说可能重要,也可能不重要
使用循环变量的地址不起作用的原因是,循环变量是在每次迭代中更新的单个位置。每次将int和struct之类的值类型分配给新变量时,都会复制它。您在这里想要实现什么?你为什么想要int指针呢?似乎您只是在尝试收集所有映射键?如果您想要该值的副本,则需要复制该值。上一个示例是一种方法,但是为什么要首先尝试获取指向for循环的索引值副本的指针呢?循环变量只分配一次。映射键/值在每次迭代时复制到同一位置。你不应该依赖于它们的地址,因为它们与基础映射中的地址没有联系。这正是我正在尝试做的。我在使用Google云数据存储的GoSDK时发现了这种情况。我需要一个
[]*datastore.Key
来传递给一些函数。我有一个map[datastore.Key]接口{}
@ralph,是的,这比int更有意义。你想在这里实现什么?你为什么想要int指针呢?似乎您只是在尝试收集所有映射键?如果您想要该值的副本,则需要复制该值。上一个示例是一种方法,但是为什么要首先尝试获取指向for循环的索引值副本的指针呢?循环变量只分配一次。映射键/值在每次迭代时复制到同一位置。你不应该依赖于它们的地址,因为它们与基础映射中的地址没有联系。这正是我正在尝试做的。我在使用Google云数据存储的GoSDK时发现了这种情况。我需要一个[]*datastore.Key
来传递给一些函数。我有一个map[datastore.Key]接口{}
@ralph,是的,这比int更有意义。我几乎试过了,但后来我认为编译器只会为Key
分配一个位置,我也会遇到同样的问题。我想我应该试试。是的,这是围棋中常见的把戏。key
的范围在循环内,因此它仅限于此迭代。不确定是什么类型的转义分析导致它被分配到堆与堆栈上,但它确实起作用。实际上,看到k:=k
以相同的名称对变量进行阴影处理也是很常见的。这不是escape分析,词法范围表示新声明对块的其余部分有效。@JimB我的意思是:如果您从未向dst
添加key
,编译器可以在每次迭代中重复使用单个堆栈位置。将其保存到切片意味着它不能被覆盖。我对编译器了解不够,不知道它是如何工作的。@captncraig:啊,我明白你的意思了。这是一个可以进行的优化,但这不是你需要知道的任何事情。如果您使用&i
,它将被转义,因此转义分析会阻止它。下面是一个不安全的示例,可以确保指针不会逃逸:;)(至少在go1.9中是这样)我几乎试过了,但后来我认为编译器只会为键
分配一个位置,我也会遇到同样的问题。我想我应该试试。是的,这是围棋中常见的把戏。key
的范围在循环内,因此它仅限于此迭代。不确定是什么类型的转义分析导致它被分配到堆与堆栈上,但它确实起作用。实际上,看到k:=k
以相同的名称对变量进行阴影处理也是很常见的。这不是escape分析,词法范围表示新声明对块的其余部分有效。@JimB我的意思是:如果您从未向dst
添加key
,编译器可以在每次迭代中重复使用单个堆栈位置。将其保存到切片意味着它不能被覆盖。我对编译器了解不够,不知道它是如何工作的。@captncraig:啊,我明白你的意思了。这是一个可以进行的优化,但这不是你需要知道的任何事情。如果您曾经使用&i