求解下面两次for结果不同的原因是什么?

codeYuanY · 2020-10-12 22:55:31 · 1143 次点击 · 大约8小时之前 开始浏览    置顶
这是一个创建于 2020-10-12 22:55:31 的主题,其中的信息可能已经有所发展或是发生改变。

type Girl struct {
    name string
}

func (g *Girl) Appointment() {
    fmt.Println("I appointment ",g.name)
}

func main(){

    friends := []Girl{{"Juliet"},{"Emily"},{"Amy"}}
    for _,friend := range friends{
        defer friend.Appointment()
    }
    /*
        打印结果:
        I appointment Amy
        I appointment Amy
        I appointment Amy
    */

    for _,friend := range friends{
        f := friend
        defer f.Appointment()
    }
    /*
        打印结果:
        I appointment Amy
        I appointment Emily
        I appointment Juliet
    */
}

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1143 次点击  
加入收藏 微博
4 回复  |  直到 2020-10-13 21:42:31
rekca
rekca · #1 · 5年之前

用friend 接收range friends时,friend的地址始终是同一个,而下面friend每次都是赋值给新的地址f。defer是后进先出,所以打印的顺序是反的。

avtion
avtion · #2 · 5年之前
  1. 第一次循环输出:使用的是for range创建的Value进行,同时因为Appointment方法传入的是Girl类型的指针,最终在defer输出时,三个Appointment方法的接收者都是Value的地址,而最终Value地址指向的是{"Amy"},即
  2. 第二次循环输出:使用的是f := friend,其中f变量进行了值拷贝,即将原切片的每个元素进行了深拷贝,最终Appointment方法的接收者是与friends切片无关的三个f变量的地址,即f1:{"Juliet"},f2:{"Emily"},f3:{"Amy"}
avtion
avtion · #3 · 5年之前

附: range会发生什么:

// The loop we generate:
len_temp := len(range)
range_temp := range    // <--- 会对切片进行引用的值拷贝
for index_temp = 0; index_temp < len_temp; index_temp++ {
    value_temp = range_temp[index_temp]
    index = index_temp
    value = value_temp
    original body
}
codeYuanY
codeYuanY · #4 · 5年之前

谢谢二位!@avtion@rekca,我基本明白了,第一种是指针行为,当defer 执行时它指向了{"Amy"},而第二种是深拷贝,每一次的f都是一个的f,而friend只是换了张皮囊而已。

        换了另一种方式,学习 
    var gf Girl
    for i := 0;i < len(friends);i++{
        gf = friends[i]
        fmt.Printf("%p\n",&gf)
        defer gf.Appointment()
    }
    /*
        打印结果:
        0xc00003a230
        0xc00003a230
        0xc00003a230
        I appointment  Amy
        I appointment  Amy
        I appointment  Amy
    */
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传