go语言-defer关键字

来源:互联网 发布:小米4c可以4g网络吗 编辑:程序博客网 时间:2024/05/17 02:35
 example1func f() (result int) {    defer func() {        result++    }()    return 0}example2func f() (r int) {     t := 5     defer func() {       t = t + 5     }()     return t}example3func f() (r int) {    defer func(r int) {          r = r + 5    }(r)    return 1}

首先要明确的是:defer是在return之前执行的。这个在 官方文档中明确说明了的。然后是了解defer的实现方式,大致就是在defer出现的地方,插入指令CALL runtime.deferproc,然后在函数返回之前的地方,插入指令CALL runtime.deferreturn。为了支持多值返回,go是用栈返回值的

return xxx这一条语句并不是一条原子指令!

整个return过程,没有defer之前,先在栈中写一个值,这个值会被当作返回值,然后再调用RET指令返回。return xxx语句汇编后是先给返回值赋值,再做一个空的return,( 赋值指令 + RET指令)。defer的执行是被插入到return指令之前的,有了defer之后,就变成了(赋值指令 + CALL defer指令 + RET指令)。而在CALL defer函数中,有可能将最终的返回值改写了…也有可能没改写。总之,如果改写了,那么看上去就像defer是在return xxx之后执行的~

上面的基础知识都有了,然后就可以说说神奇的defer了。告诉大家一个简单的转换规则大家就再也不为defer迷糊了。改写规则是将return语句分开成两句写,return xxx会被改写成:

返回值 = xxx
调用defer函数
空的return

 先看example1,它可以改写成这样:func f() (result int) {     result = 0  //return语句不是一条原子调用,return xxx其实是赋值+RET指令     func() { //defer被插入到return之前执行,也就是赋返回值和RET指令之间         result++     }()     return}所以这个返回值是1。再看example2,它可以改写成这样:func f() (r int) {     t := 5     r = t //赋值指令     func() {//defer被插入到赋值与返回之间执行,这个例子中返回值r没被修改过         t = t + 5     }     return        //空的return指令}所以这个的结果是5。最后看example3,它改写后变成:func f() (r int) {     r = 1  //给返回值赋值     func(r int) {        //这里改的r是传值传进去的r,不会改变要返回的那个r值          r = r + 5     }(r)     return        //空的return}所以这个例子的结果是1

再看一个例子

func do() (ok bool) {  file,_ := os.Open("c:\a.txt")  defer file.Close()  // doSomething  return ok}

我们可以在官方的文档中看到defer的执行顺序是逆序的,也就是先进后出的顺序:

for i := 0; i < 5; i++ {  defer fmt.Printf("%d ", i)}

打印结果是:4,3,2,1,0

原创粉丝点击