若有任何问题或建议,欢迎及时交流和碰撞。我的公众号是 【脑子进煎鱼了】,GitHub 地址:https://github.com/eddycjy。
大家好,我是煎鱼。
前几天在读者交流群里看到一位小伙伴,在向大家咨询 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,又或是其他答案?
欢迎大家在下方评论区留言讨论和分享解题的思路,一起思考和进步。
分享 Go 语言、微服务架构和奇怪的系统设计,欢迎大家关注我的公众号和我进行交流和沟通。
最好的关系是互相成就,各位的点赞就是煎鱼创作的最大动力,感谢支持。
前言 前几篇我们一起学习了基于数组、链表、二叉树、红黑树来实现Map的操作,本...
这是第 98 篇不掺水的原创,想获取更多原创好文,请搜索公众号关注我们吧~ 本文...
先点赞再看,养成好习惯 前言 在网络请求中,由于网络是不可靠的,所以经常会有...
一、CSS变量带来的质变 CSS变量带来的提升绝不仅仅是节约点CSS代码,以及降低CSS...
本文已经收录进 SpringBootGuide (SpringBoot2.0+从入门到实战!) Github地址: ...
Dreamweaver怎么给字体加粗,下面我们就来看看详细的教程。 软件名称: Adobe Dr...
当你看到input这个html标签的时候,你会想到什么?一个文本框?一个按钮?一个单...
上一篇文章中,我们从 packges/vue/src/index.ts 的入口开始,了解了一个 Vue 对...
Dreamweaver使用临时文件将一些未经保存的数据传输至其它应用程序中,例如用户可...
近期公司的多个网站都是使用Velocaity研发的,产生了很多扩展名为vm的文件。该文...