go滥用指针

864 阅读2分钟

正常demo1

type student struct {
	Name string
	Age  int
}


func main()  {
	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
	}

	for k,v:=range m{
	    fmt.Println(k,"=>",v)
	}
}

输出

zhou => {zhou 24}
li => {li 23}
wang => {wang 22}

得到m后,如果对stus进行值修改,比如

stus[0].Age = 1000
//再次输出
for k,v:=range m{
    fmt.Println(k,"=>",v)
}

得到结果为,

zhou => {zhou 24}
li => {li 23}
wang => {wang 22}
zhou => {zhou 24}
li => {li 23}
wang => {wang 22}

证明以上为值传递写法,修改源stus,并不影响m

滥用指针 demo2?

type student struct {
	Name string
	Age  int
}


func main()  {
	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      //如果不加“&”会报错
	}

	for k,v:=range m{
	    fmt.Println(k,"=>",*v) //需要打印出值
	}
}

输出

zhou => {wang 22}
li => {wang 22}
wang => {wang 22}

很明显,传递的是一个变量stu的指针,而不是值;因为这个变量的值始终在变,但变量的指针不变;导致打印出的是变化的最终值。
接下来正确方法,

for _, stu := range stus {
    m[stu.Name] = &stu      
}

=>

for k, stu := range stus {
    m[stu.Name] = &stus[k]
}

这样,把stus值得每个元素的指针传递给了m种的值(指针),这样就输出的就是对了,

zhou => {zhou 24}
li => {li 23}
wang => {wang 22}

得到了m后,如果再对stus得值进行修改,比如

stus[0].Age = 1000

最终得到结果,也跟着变化

li => {li 23}
wang => {wang 22}
zhou => {zhou 1000}

证明以上分析正确性。

总结

  • 对比以上两种方式,第一种要安全一些,第二种性能好一些;但提倡第一种写法;能不显式定义指针就不要定义。