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