range中遍历的变量是一个副本
在range中无法对变量进行修改
package main
import "fmt"
type student struct {
id string
name string
sex rune
}
func (s student) Show() {
fmt.Println("id:", s.id, "name:", s.name, "sex:", s.sex)
}
func main() {
ss := []student{
{"1", "张三", '男'},
{"2", "李四", '女'},
{"3", "王五", '女'},
{"4", "赵六", '男'},
{"5", "孙七", '女'},
}
for _, s := range ss {
s.name = "张六" //将所有人名字改成这个
}
fmt.Println(ss)
}
//输出,可以看出这些名字都没有变化
[{1 张三 30007} {2 李四 22899} {3 王五 22899} {4 赵六 30007} {5 孙七 22899}]
使用defer时可能出现的问题
package main
import "fmt"
type student struct {
id string
name string
sex rune
}
func (s *student) Show() {
fmt.Println("id:", s.id, "name:", s.name, "sex:", s.sex)
}
func main() {
ss := []student{
{"1", "张三", '男'},
{"2", "李四", '女'},
{"3", "王五", '女'},
{"4", "赵六", '男'},
{"5", "孙七", '女'},
}
for _, s := range ss {
defer s.Show()
}
}
对于以上这段代码,我们希望输出
id: 5 name: 孙七 sex: 22899
id: 4 name: 赵六 sex: 30007
id: 3 name: 王五 sex: 22899
id: 2 name: 李四 sex: 22899
id: 1 name: 张三 sex: 30007
但实际上会输出
id: 5 name: 孙七 sex: 22899
id: 5 name: 孙七 sex: 22899
id: 5 name: 孙七 sex: 22899
id: 5 name: 孙七 sex: 22899
id: 5 name: 孙七 sex: 22899
由于defer是将要执行的对象拷贝到栈中,再依次出栈,因此顺着这个思路,我们对range变量中每个打印地址,发现地址都是一样的
for _, s := range ss {
fmt.Printf("%p\n", &s)
}
//输出
0xc0000b4360
0xc0000b4360
0xc0000b4360
0xc0000b4360
0xc0000b4360
因此推测range的实现是类似
var tmp student
for i := 0; i < len(ss); i++ {
tmp = ss[i]
defer tmp.Show()
}
那么既然是对每个元素拷贝之后放到栈中,为什么变成对最后一个元素拷贝了5次呢?经过实验发现,问题在于Show方法定义的接收者参数是指针类型
func (s *student) Show() {
fmt.Println("id:", s.id, "name:", s.name, "sex:", s.sex)
}
因此在执行defer时压入栈中的是接收者的地址,而由于range的实现方法,相当于压入栈中的实际上是tmp的地址
var tmp student
for i := 0; i < len(ss); i++ {
tmp = ss[i]
defer tmp.Show()//也就是说tmp的地址被压栈了5次
}
因此解决方法就是去掉’*’,即可得到我们想要的结果
func (s student) Show() {
fmt.Println("id:", s.id, "name:", s.name, "sex:", s.sex)
}
func main() {
ss := []student{
{"1", "张三", '男'},
{"2", "李四", '女'},
{"3", "王五", '女'},
{"4", "赵六", '男'},
{"5", "孙七", '女'},
}
for _, s := range ss {
defer s.Show()
}
}
//输出
id: 5 name: 孙七 sex: 22899
id: 4 name: 赵六 sex: 30007
id: 3 name: 王五 sex: 22899
id: 2 name: 李四 sex: 22899
id: 1 name: 张三 sex: 30007