将 map 的值指针添加到 slice 的行为
behavior of map's value pointer being added to slice
func TestMapValuePointer2(t *testing.T) {
fmt.Println("Test Map Value Pointer 2")
m := map[string]int{"rsc": 3711, "r": 2138, "gri": 1908, "adg": 912}
n := len(m)
array := make([]*int, n)
i := 0
for _, v := range m {
array[i] = &v
fmt.Printf("Add to array: %d\n", v)
i++
}
for _, k := range array {
fmt.Printf("Value: %d\n", *k)
}
}
输出不是:
价值:912
价值:3711
价值:2138
价值:1908
相反,输出可能是这样的:
价值:912
价值:912
价值:912
价值:912
谁能解释一下为什么结果像上面这样?
引用自this doc:
[...] each iteration of the loop uses the same instance of the variable v, so each closure shares that single variable [...]
换句话说,循环变量 v
在所有迭代中重复使用,因此您将同一个指针分配给所有切片元素。
在循环中声明的变量不会像那样被回收,所以这将如您所料工作:
for _, v := range m {
vv := v
array[i] = &vv
fmt.Printf("Add to array: %d\n", v)
i++
}
顺便说一句,您还没有解释为什么要使用 *int
作为值的类型。如果您只使用 int
值,所有这些都会更简单。
这里的问题是在循环期间创建的变量 v
实际上是我们在地图中拥有的值的副本。与 v
相关的内存在循环开始时分配一次,稍后会重新用于其他值,因为此处指向的最后一个值是 912,在这种情况下,您会在数组中看到 912。
对此的一个简单证明是:https://play.golang.org/p/K0yAbEIf3G
解决此问题的一个方法是将地图值更改为 *int 指针而不是值,稍后我们可以取消引用它们以获取实际值:
package main
import "fmt"
func main() {
fmt.Println("Test Map Value Pointer 2")
a, b, c, d := 3711, 2138, 1908, 912
m := map[string]*int{"rsc": &a, "r": &b, "gri": &c, "adg": &d}
n := len(m)
array := make([]*int, n)
i := 0
for _, v := range m {
array[i] = v
fmt.Printf("Add to array: %d\n", *v)
i++
}
for _, k := range array {
fmt.Printf("Value: %d\n", *k)
}
}
func TestMapValuePointer2(t *testing.T) {
fmt.Println("Test Map Value Pointer 2")
m := map[string]int{"rsc": 3711, "r": 2138, "gri": 1908, "adg": 912}
n := len(m)
array := make([]*int, n)
i := 0
for _, v := range m {
array[i] = &v
fmt.Printf("Add to array: %d\n", v)
i++
}
for _, k := range array {
fmt.Printf("Value: %d\n", *k)
}
}
输出不是:
价值:912 价值:3711 价值:2138 价值:1908
相反,输出可能是这样的:
价值:912 价值:912 价值:912 价值:912
谁能解释一下为什么结果像上面这样?
引用自this doc:
[...] each iteration of the loop uses the same instance of the variable v, so each closure shares that single variable [...]
换句话说,循环变量 v
在所有迭代中重复使用,因此您将同一个指针分配给所有切片元素。
在循环中声明的变量不会像那样被回收,所以这将如您所料工作:
for _, v := range m {
vv := v
array[i] = &vv
fmt.Printf("Add to array: %d\n", v)
i++
}
顺便说一句,您还没有解释为什么要使用 *int
作为值的类型。如果您只使用 int
值,所有这些都会更简单。
这里的问题是在循环期间创建的变量 v
实际上是我们在地图中拥有的值的副本。与 v
相关的内存在循环开始时分配一次,稍后会重新用于其他值,因为此处指向的最后一个值是 912,在这种情况下,您会在数组中看到 912。
对此的一个简单证明是:https://play.golang.org/p/K0yAbEIf3G
解决此问题的一个方法是将地图值更改为 *int 指针而不是值,稍后我们可以取消引用它们以获取实际值:
package main
import "fmt"
func main() {
fmt.Println("Test Map Value Pointer 2")
a, b, c, d := 3711, 2138, 1908, 912
m := map[string]*int{"rsc": &a, "r": &b, "gri": &c, "adg": &d}
n := len(m)
array := make([]*int, n)
i := 0
for _, v := range m {
array[i] = v
fmt.Printf("Add to array: %d\n", *v)
i++
}
for _, k := range array {
fmt.Printf("Value: %d\n", *k)
}
}