指针、slice、map、chan是go的引用类型,struct是值类型。函数中如果需要修改struct本身的值,需要传递struct指针作为参数。同时,如果声明值为struct的map,按key修改值时,也需要注意将map的value声明为struct指针类型,比如:
packagemainimport"fmt"typestudentstruct{AgeintNamestring}funcmain(){m:=make(map[string]student)m["s1"]=student{30,"s1"}m["s2"]=student{31,"s2"}m["s3"]=student{32,"s3"}fmt.Println(m)m["s1"].Age=33fmt.Println(m)}
上面代码试图通过key修改对应结构体成员变量的值。结果编译时报错:
#command-line-arguments./test.go:17:14:cannotassigntostructfieldm["s1"].Ageinmap
因为struct是值类型的,m["s1"]指向的不是实际的struct对象,go编译器这里直接给出了错误提示。将map值类型修改为student指针,可以解决此问题。
packagemainimport"fmt"typestudentstruct{AgeintNamestring}funcmain(){m:=make(map[string]*student)m["s1"]=&student{30,"s1"}m["s2"]=&student{31,"s2"}m["s3"]=&student{32,"s3"}fmt.Println(m)m["s1"].Age=33fmt.Println(m)}
此时m["s1"]指向的是具体的指针对象,可以通过指针对象修改其成员变量。再接着上面的例子,遍历访问该map,
packagemainimport"fmt"typestudentstruct{AgeintNamestring}funcmain(){m:=make(map[string]*student)m["s1"]=&student{30,"s1"}m["s2"]=&student{31,"s2"}m["s3"]=&student{32,"s3"}fmt.Println(m)m["s1"].Age=33fork,v:=rangem{fmt.Println(k,*v)}}
运行该程序会发现每次的输出顺序都不同,第一次运行,
map[s1:0xc00000c060s2:0xc00000c080s3:0xc00000c0a0]s1{33s1}s2{31s2}s3{32s3}
再运行一次,
map[s1:0xc000166000s2:0xc000166020s3:0xc000166040]s2{31s2}s3{32s3}s1{33s1}
这是因为在底层实现上,range map调用了mapiterinit方法生成迭代器,在生成迭代器的过程中,首先产生一个随机数,并根据该随机数确定首先遍历的bucket的位置。所以每次产生的key顺序不同。