前言:连续2天转多篇Go语言基础,这也正常,刚学嘛,转载别人的,不发原创总是对的,这不,我忍不住了,上篇自己原创的思考吧!
Go语言之defer
来看下官方定义
A “defer” statement invokes a function whose execution is deferred to the moment the surrounding function returns, either because the surrounding function executed a return statement, reached the end of its function body, or because the corresponding goroutine is panicking.
emm……不知道你看懂了么,不准查翻译哦,给出我个人的理解
defer语句其实就是一个函数调用,只不过这个调用有点特殊,它不会立即执行,会延迟到defer语句所在的函数快要执行完毕后执行。
何为快要执行完毕后执行呢?文字总是表达不不够到位,难以理解,看下代码吧。
package main
import "fmt"
//defer深入理解 https://haosy.blog.csdn.net/
func main() {
fmt.Println("开始")
defer fmt.Println("Do……")
fmt.Println("结束")
}
可以看到,defer fmt.Println("Do……")
发生了延迟,等待函数块(这里指main函数)执行完其它语句再执行。
那么,读者你可能要想要如果不是一个defer呢?而是多个defer呢?那么当函数块执行完其它语句后,这多个defer的执行顺序又是什么呢?多个defer的执行顺序为:后进先出,先进后出(遵循栈结构)
package main
import "fmt"
//defer深入理解 https://haosy.blog.csdn.net/
func main(){
fmt.Println("开始")
defer fmt.Println("Do……第1个进入的")
defer fmt.Println("Do……第2个进入的")
defer fmt.Println("Do……第3个进入的")
fmt.Println("结束")
}
defer语言能用到哪里呢?
因defer语句延迟调用的特性(会延迟到defer语句所在的函数快要执行完毕后执行),所以defer语句能非常方便的处理资源释放问题。比如:资源清理、文件关闭、解锁及记录时间等。
等等等等,你以为defer你这就学会了么?测试一下自己,如果做对了,下文就不用再看了,如果做的不对,那么我这篇文章的正文对你而说才正式开始!
package main
import "fmt"
func f1() int {
x := 4
defer func() {
x++
}()
return x
}
func f2() (x int) {
defer func() {
x++
}()
return 4
}
func f3() (y int) {
x := 4
defer func() {
x++
}()
return x
}
func f4() (x int) {
defer func(x int) {
x++
}(x)
return 4
}
func main() {
fmt.Println("f1()的结果为:", f1())
fmt.Println("f2()的结果为:", f2())
fmt.Println("f3()的结果为:", f3())
fmt.Println("f4()的结果为:", f4())
}
你说结果是多少呢?
如果你答对了,恭喜你,掌握了,可以转身离开了,如果答错了,就看下述正文。(偷偷告诉你,我第一次做,第二次做都答错了,这不丢脸)
知识补充,Go语言中的return语句
在Go语言的函数中return语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步。而defer语句执行的时机就在返回值赋值操作后,RET指令执行前。具体如下图所示:
有点绕口哈,什么意思呢?就是说 return x 不是一个原子操作,而是拆开的,先给返回值x赋值,defer
,然后再ret,而正是因为如此(做错题可能就是因为这里不清楚)。
分析上文测试题
func f1() int {
x := 4
defer func() {
x++
}()
return x
}
可以看到x:=4,然后reutn x ,给返回值赋值4,然后去执行defer
,这个时候x++,x变成了5,但是可以看到func f1() int {
这里并没有给返回值命名,也就是说 返回值 还是一开始赋予的4,defer修改的只是x的值,而不是返回值的值。相信给你绕晕了,不要着急,你继续往下看,把所有例题都看完,回过头来,你就明白了。f1()的结果为: 4
func f2() (x int) {
defer func() {
x++
}()
return 4
}
可以看到 return 4,再看下这个func f2() (x int)
,此函数给返回值命名为x了,那么return 4 实际是什么呢?
返回值为4,把4赋予给了x,然后去执行defer
,x++,这x不就是返回值的x么,所以x变成了5,也就是返回值变成了5,怎么样,哈哈,我骗你了?是不是更迷糊了,不要着急,如果例子都看完,还迷糊,我就去吃一口土,只要你留言,我就吃。f2()的结果为: 5
func f3() (y int) {
x := 4
defer func() {
x++
}()
return x
}
可以看到func f3() (y int) {
,此函数给返回值命名为y了,那么,刚开始 x:=4,执行return x,也就是给返回值y赋值4,然后去执行defer
,可是你看到了么?这defer中修改的都是x,和y没有关系啊,也就是和返回值没有关系啊,y的值(返回值)没有变动,还是4f3()的结果为: 4
func f4() (x int) {
defer func(x int) {
x++
}(x)
return 4
}
可以看到func f4() (x int)
,此函数给返回值命名为x了,那么这里的return 4 不就是 返回值=x=4么,然后去执行defer,有趣的是看到了么,defer…{}(x),把x当作参数传进去了,值类型,参数发生了修改,原本的值是不会修改的(改变的是defer函数中x的副本),所以返回值也就没有发生变动还是4f4()的结果为: 4
舒舒服服,懂了没?加把劲再来几道题吧!你可别走,看完再走也不迟!
//传一个指针到匿名函数中
func f5() (x int) {
defer func(x *int) {
(*x)++
}(&x)
return 4 //1.返回值=x=4 2.defer x=5 3.RET返回
}
不过多分析了,f5()和f4()可以看到一个是值传进去了,一个是指针传进去了,引用类型和值类型如果你懂的话,这里不用过多分析f5()的结果为: 5
留下来一道面试题,在评论区留出你的答案吧
func calc(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret)
return ret
}
func main() {
x := 1
y := 2
defer calc("AA", x, calc("A", x, y))
x = 10
defer calc("BB", x, calc("B", x, y))
y = 20
}
问,上面代码的输出结果是?(提示:defer注册要延迟执行的函数时该函数所有的参数都需要确定其值)