go语言坑之for range

@爱耍流氓的唐僧  October 22, 2021

1.看下下面的demo

type student struct {
    name string
    age  int
}

func main() {
    m := make(map[string]*student)
    stus := []student{
        {name: "小王子", age: 18},
        {name: "娜扎", age: 23},
        {name: "大王八", age: 9000},
    }

    for _, stu := range stus {
        m[stu.name] = &stu
    }
    for k, v := range m {
        fmt.Println(k, "=>", v.name)
    }
}

输出是:

娜扎 => 大王八
大王八 => 大王八
小王子 => 大王八

看了下不是我们想要的结果,这是为什么呢?
这其实就是for range的一个坑?
再看下下面的代码:

package main

import "fmt"

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

    for index, value := range slice {
        myMap[index] = &value
    }
    fmt.Println("=====new map=====")
    prtMap(myMap)
}

func prtMap(myMap map[int]*int) {
    for key, value := range myMap {
        fmt.Printf("map[%v]=%v\n", key, *value)
    }
}

运行程序输出:

=====new map=====
map[3]=3
map[0]=3
map[1]=3
map[2]=3

但是由输出可以知道,映射的值都相同且都是3。其实可以猜测映射的值都是同一个地址,遍历到切片的最后一个元素3时,将3写入了该地址,所以导致映射所有值都相同。其实真实原因也是如此,因为for range创建了每个元素的副本,而不是直接返回每个元素的引用,如果使用该值变量的地址作为指向每个元素的指针,就会导致错误,在迭代时,返回的变量是一个迭代过程中根据切片依次赋值的新变量,所以值的地址总是相同的,导致结果不如预期,准确来说就是,在for range这个循环中,定义了这个value变量,每次都是引用的同样的地址,所以值变成了最后的3。

看下下面的代码你就明白了

package main

import "fmt"

func main() {
    slice := []int{0, 1, 2, 3}
    myMap := make(map[int]*int)
    var num int
    for index, value := range slice {
        num = value
        myMap[index] = &num
    }
    fmt.Println("=====new map=====")
    prtMap(myMap)
}

func prtMap(myMap map[int]*int) {
    for key, value := range myMap {
        fmt.Printf("map[%v]=%v\n", key, *value)
    }
}

修正后

package main

import "fmt"

func main() {
    slice := []int{0, 1, 2, 3}
    myMap := make(map[int]*int)
    for index, value := range slice {
        num := value
        myMap[index] = &num
    }
    fmt.Println("=====new map=====")
    prtMap(myMap)
}

func prtMap(myMap map[int]*int) {
    for key, value := range myMap {
        fmt.Printf("map[%v]=%v\n", key, *value)
    }
}

总结:在for range循环的时候如果遇到指针的时候需要重新定义变量,以免引用同一个地址的变量,数据error。


添加新评论