Go 群友提问:学习 defer 时很懵逼,这道不会做!

大家好,我是煎鱼。

前几天在读者交流群里看到一位小伙伴,在向大家咨询 Go 相关的技术问题。疑问是:“各位大佬,我在学习 defer 遇到闭包的时候很懵逼,谁比较明白,能指点?

疑问

他的疑问是下面这道 Go 语言的 defer 题目,大家一起看看:

func main() {
 var whatever [6]struct{}
 for i := range whatever {
  defer func() {
   fmt.Println(i)
  }()
 }
}

请自己先想一下输出的结果答案是什么。

这位小伙伴按自己的理解后,认为应当输出 xx。但最终的输出结果,可能与其思考的有所偏差,一时想不通。

解惑

这段程序的输出结果是:

5
5
5
5
5
5

为什么全是 5,为什么不是 0, 1, 2, 3, 4, 5 这样的输出结果呢?

其根本原因是闭包所导致的,有两点原因:

  • for 循环结束后,局部变量 i 的值已经是 5 了,并且 defer的闭包是直接引用变量的 i。

  • 结合defer 关键字的特性,可得知会在 main 方法主体结束后再执行。

结合上述,最终输出的结果是已经自增完毕的 5。

进一步思考

既然了解了为什么,我们再变形一下。再看看另外一种情况,代码如下:

func main() {
 var whatever [6]struct{}
 for i := range whatever {
  defer func(i int) {
   fmt.Println(i)
  }(i)
 }
}

与第一个案例不同,我们这回把变量 i 传了进去。那么他的输出结果是什么呢?

这段程序的输出结果是:

5
4
3
2
1
0

为什么是 5, 4, 3, 2, 1, 0 呢,为什么不是 0, 1, 2, 3, 4, 5?(难道煎鱼敲错了吗?)

其根本原因在于两点:

  • for 循环时,局部变量 i 已经传入进 defer func 中 ,属于值传递。其值在 defer 语句声明时的时候就已经确定下来了。

  • 结合 defer 关键字的特性,是按先进后出的顺序来执行的。

结合上述,最终输出的结果是 5, 4, 3, 2, 1, 0。

下一个疑问

没过一会,这位小伙伴又有了新的感悟。抛出了新的示例问题,如下:

func f1() (r int) {
   defer func() {
      r++
   }()
   return 0
}

func f2() (r int) {
   t := 5
   defer func() {
      t = t + 5
   }()
   return t
}

func f3() (r int) {
   defer func(r int) {
      r = r + 5
   }(r)
   return 1
}

主函数:

func main() {
 println(f1())
 println(f2())
 println(f3())
}

请自己先想一下输出的结果答案是什么。

这段程序的输出结果是:

1
5
1

为什么是 1, 5, 1 呢,而不是 0, 10, 5,又或是其他答案?

欢迎大家在下方评论区留言讨论和分享解题的思路,一起思考和进步。


关注煎鱼公众号,吸取精华:

???? 点击关注煎鱼,在知识的海洋里遨游

学习资料分享,关注公众号回复指令:

  • 回复【000】,下载 LeetCode 题解大全。

  • 回复【001】,下载 Go 进阶图书 Mastering Go。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值