Go语言关于defer,recover函数实例

遇到这样一种情况:

func f(n int) (r int) {
    defer func() {
        r += n
        recover()
    }()

    var f func()

    defer f()
    f = func() {
        r += 2
    }
    return n + 1
}

func main() {
    fmt.Println(f(3))
}

输出:7
跟预想的完全不一样呀,颠覆了之前对defer()的认知
为了查看具体的过程及运行顺序原理,加了些日志:

func f(n int) (r int) {
	defer func() {
		fmt.Println("第一处:",r)
		r = r + n
		fmt.Println("第二处:",r)
		recover()
	}()
	var f func()
	defer f()
	f = func() {
		fmt.Println("第三处:",r)
		r = r + 2
		fmt.Println("第四处:",r)
	}
	fmt.Println("最外面:",r)
	return n + 1
}

func main() {
	fmt.Println(f(3))
}

输出:

最外面: 0
第一处: 4
第二处: 7
7

果然跟预想的不一致
按理来说,defer语句不是应该先执行后面的那个defer再执行前面的么,为啥后面的defer根本就没执行呢
咱们一个一个分析:
首先调用f(3)这个函数,此时n=3,r=0
然后开始声明第一个defer 语句,此时值没改变,然后声明一个函数f。
接着声明第二个defer,其中第二个defer语句中调用了f()函数,因为在defer之前没有具体定义f()函数,所以在第二个defer的时候f()其实就是空的,这也就是为什么没有打印出第三处第四处的原因。跟之前的理解一样(原来是我没有认真看代码的语音,尴尬了)
之后打印:最外面:0,因为此时还没有给r进行赋值操作,只是初始化了一个零值又因为是int类型的所以为0
在这块大家要理解一个点就是return和defer的执行顺序:
先为返回值赋值,然后执行defer,然后return到函数调用处。
既然这样,那咱们继续,之前r=0,然后执行return后面的东西,也就是r=n+1,此时r=4,如果没有defer的话此处就应该直接返回了。然后咱们接着执行,因为defer语句是先进后出的,所以先执行第二个defer语句,刚才说了,这块缓存的是一个空函数,那当然就什么不输出了,也就没有修改的操作,所以此时r的值依旧是4,第二个defer执行完了,然后咱们继续第一个defer语句,先打印出:第一处:4然后对r进行修改,r=r+n,然后r的值变为7,打印第二处:7,最后这个recover()其实也就是恢复程序,我的理解就是不管你抛什么panic,这块都接收到然后恢复程序继续运行,其实到这块程序已经结束了
现在咱们再看一下这个recover(),如果把这块注释了呢?程序到底报的什么错?需要用到recover()

最外面: 0
第一处: 4
第二处: 7
panic: runtime error: invalid memory address or nil pointer dereference

可以看到报出来的是一个无效的内存地址及空指针引用,我在想我哪块有空指针呢?
对了,在第二个defer的时候,程序其实调用的是一个空函数,只是声明了函数,其实没有分配内存的,打印一下内存地址,发现是个0,这也就验证了咱们刚才说的,所以这个recover()恢复的就是这块的panic
知道了之后那咱们修改一下代码:

func f(n int) (r int) {
	defer func() {
		fmt.Println("第一处:",r)
		r = r + n
		fmt.Println("第二处:",r)
		recover()
	}()
	f := func() {
		fmt.Println("第三处:",r)
		r = r + 2
		fmt.Println("第四处:",r)
	}
	defer f()
	fmt.Println("最外面:",r)
	return n + 1
}

func main() {
	fmt.Println(f(3))
}

现在把函数定义到第二个defer之前,输出:

最外面: 0
第三处: 4
第四处: 6
第一处: 6
第二处: 9
9

完全跟预想的一样,先执行第二个defer里面的,然后执行第一个defer里面的,此时就可以把recover()干了

总结:
1.defer和return的执行顺序:先为返回值赋值,然后执行defer,然后return到函数调用处
2.defer执行顺序是先进后出

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值