以下代码运行,会输出什么?
package main
import "fmt"
type student struct {
Name string
Age int
}
func pase_student() {
m := make(map[string]*student)
stus := []student{
{Name: "A", Age: 1},
{Name: "B", Age: 2},
{Name: "C", Age: 3},
}
for _, stu := range stus {
m[stu.Name] = &stu
}
for k, v := range m {
fmt.Printf("%v,%v,%v\n", k, v.Name, v.Age)
}
}
func main() {
pase_student()
}
请思考,答案在文末
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
答案
A,C,3
B,C,3
C,C,3
为什么?
因为在golang
的for range
中,临时变量stu
会被复用,然后将新的值复制进去,而不会另开辟空间。所以题目中m[stu.Name] = &stu
,会导致stu
中的值会是最后一次复制进去的值,即stus
最后一个值。
我们可以写个测试程序,验证这个“坑”:
func main() {
a := []int{1, 2, 3, 4}
for x, n := range a {
fmt.Printf("%d: %d\n", x, n)
fmt.Printf("%p\n", &n)
}
}
输出
0: 1
0xc0000160c8
1: 2
0xc0000160c8
2: 3
0xc0000160c8
3: 4
0xc0000160c8
可以看到,每次&n
都是固定的
如何修改?
第1种,使用值,而不是指针。可以将 m := make(map[string]*student)
改成 m := make(map[string]student)
, 完整代码如下:
func pase_student() {
m := make(map[string]student)
stus := []student{
{Name: "A", Age: 1},
{Name: "B", Age: 2},
{Name: "C", Age: 3},
}
for _, stu := range stus {
m[stu.Name] = stu
}
for k, v := range m {
fmt.Printf("%v,%v,%v\n", k, v.Name, v.Age)
}
}
第2种,不用for range
改用经典的for
func pase_student() {
m := make(map[string]student)
stus := []student{
{Name: "A", Age: 1},
{Name: "B", Age: 2},
{Name: "C", Age: 3},
}
for i := 0; i < len(stus); i++ {
m[stus[i].Name] = stus[i]
}
for k, v := range m {
fmt.Printf("%v,%v,%v\n", k, v.Name, v.Age)
}
}
其他方式也可以。
所以,在实际项目开发中,用到for range
时赋值时一定要注意这个“坑”