下面这段代码输出什么,说明原因。

func main() {
    slice := []int{0,1,2,3}
    m := make(map[int]*int)

    for key,val := range slice {
        m[key] = &val
    }

    for k,v := range m {
        fmt.Println(k,"->",*v)
    }
}

注意此类问题在Go版本在1.22 版本后将会有变化

🔗:修复 Go 1.22 中的 For 循环

Go >=1.22

0 -> 0
1 -> 1
2 -> 2
3 -> 3

解释:Go <= 1.22 当 for key,val := range slice 时,每次循环都会创建新变量 keyval。从而实现每次循环的 keyval变量的地址都是不同的。

在源代码上增加验证打印

package main

import "fmt"

func main() {
    slice := []int{0, 1, 2, 3}
    m := make(map[int]*int)
    for key, val := range slice {
        m[key] = &val
        fmt.Println("key的值", key, "key的地址", &key, "slice[", key, "] 的地址", &slice[key], "val 的地址", &val)
    }
    fmt.Println("m集合的内容", m)
    for k, v := range m {
        fmt.Println(k, "->", *v)
    }
}
//GO版本 1.22.4
key的值 0 key的地址 0xc00000a0e0 slice[ 0 ] 的地址 0xc000014200 val 的地址 0xc00000a0c8
key的值 1 key的地址 0xc00000a110 slice[ 1 ] 的地址 0xc000014208 val 的地址 0xc00000a0e8
key的值 2 key的地址 0xc00000a120 slice[ 2 ] 的地址 0xc000014210 val 的地址 0xc00000a118
key的值 3 key的地址 0xc00000a130 slice[ 3 ] 的地址 0xc000014218 val 的地址 0xc00000a128
m集合的内容 map[0:0xc00000a0c8 1:0xc00000a0e8 2:0xc00000a118 3:0xc00000a128]
0 -> 0
1 -> 1
2 -> 2
3 -> 3

Go <1.22

0 -> 3
1 -> 3
2 -> 3
3 -> 3

解析:当 for key,val := range slice 时,是创建了新变量 keyvalval变量的值 = 在每笔循环中都被赋值为slice元素的值。 当代码运行到m[key] = &val, m[key] 的值 = val变量的地址。共遍历了4轮,每轮都存储的是val变量的地址,又因为在每轮for中val的地址是不变的,但是val的地址指向的值在不断被赋值为slice元素的值 [0->1->2->3]。因此for循环结束后m集合中值地址指向都为3

在源代码上增加验证打印

package main

import "fmt"

func main() {
    slice := []int{0, 1, 2, 3}
    m := make(map[int]*int)
    for key, val := range slice {
        m[key] = &val
        fmt.Println("key的值", key, "key的地址", &key, "slice[", key, "] 的地址", &slice[key], "val 的地址", &val)
    }
    fmt.Println("m集合的内容", m)
    for k, v := range m {
        fmt.Println(k, "->", *v)
    }
}
//GO版本 1.20.5
key的值 0 key的地址 0xc00000a0c8 slice[ 0 ] 的地址 0xc00000e220 val 的地址 0xc00000a0e0
key的值 1 key的地址 0xc00000a0c8 slice[ 1 ] 的地址 0xc00000e228 val 的地址 0xc00000a0e0
key的值 2 key的地址 0xc00000a0c8 slice[ 2 ] 的地址 0xc00000e230 val 的地址 0xc00000a0e0
key的值 3 key的地址 0xc00000a0c8 slice[ 3 ] 的地址 0xc00000e238 val 的地址 0xc00000a0e0
m集合的内容 map[0:0xc00000a0e0 1:0xc00000a0e0 2:0xc00000a0e0 3:0xc00000a0e0]
0 -> 3
1 -> 3
2 -> 3
3 -> 3

知识点: for range 循环的时候会创建每个元素的副本,而不是元素的引用

参考资料: