Golang range的坑

594 阅读1分钟

以下代码有什么问题,说明原因。

type student struct {
    Name string
    Age  int
}

func pase_student() {
    m := make(map[string]*student)
    stus := []student{
        {Name: "zhou", Age: 24},
        {Name: "li", Age: 23},
        {Name: "wang", Age: 22},
    }
    for _, stu := range stus {
        m[stu.Name] = &stu
    }
}

最后的结果是

map[li:0xc00008a000 wang:0xc00008a000 zhou:0xc00008a000]

气不气,最后Map中的student指针全都指向了同一个地址,为什么呢?

我们在代码打印一些信息

func pase_student() {
	stu1 := student{Name: "zhou", Age: 24}
	stu2 := student{Name: "li", Age: 23}
	stu3 := student{Name: "wang", Age: 22}

	println(&stu1,&stu2,&stu3)

	stus := []student{stu1, stu2, stu3}
	for index, stu := range stus {
		println(index, "=", &stu)
	}
}

输出结果

0xc000034700 0xc0000346e8 0xc0000346d0
0 = 0xc000034718
1 = 0xc000034718
2 = 0xc000034718

可以发现在range中打印的stu指针都是同一个,而且不是前面初始化的3个stu中的任何一个地址

可以这么理解,range是遍历这个slice,range申请了一块内存来存放元素,每个元素都会复制到这块地址上,所以在range中打印的地址都是range申请的这块内存地址,最后这块地址的值就是slice最后一个元素的值

而正确的方式是

for i:= 0;i < len(stus); i++ {
  m[stus[i].Name] = &stus[i]
}
for k,v:= range m {
  println(k,"=>",v.Name)
}