zoukankan      html  css  js  c++  java
  • golang中defer精析

    本文转自chen yuwen的博客:http://my.oschina.net/yuwenc/blog/300592

    example1

    func f() (result int) { 
        defer func() { 
            result++ 
        }() 
        return 0
    }
    example2

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

    example3

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

    先不要运行代码,自己在心里跑一遍结果。然后再去验证。如果三个都做对了并且不是蒙的....好吧,不用往下看了,你已经懂defer了。




    多空几行确保你先在心里跑过一遍代码,之后验证了,并且存在疑惑......

    额,如果example1中你算的是0,你就错了

    如果example2中你觉得是10,你又错了...蒙对的不算...

    如果example3中觉得得6,你又错了...如果你有算对的,也有算错,好吧...你丫的就是在蒙!

    不懂的继续往下看啊.....


    首先要明确的是:defer是在return之前执行的

    这是官方文档中明确说明的http://golang.org/ref/spec#Defer_statements ,知道就行了,可以无视


    然后要了解是的defer的实现方式,我以前有写过http://bbs.mygolang.com/thread-271-1-1.html

    大意就是在defer出现的地方插入的指令

    CALL runtime.deferproc

    然后在函数返回之前的地方,插入指令

    CALL runtime.deferreturn


    再就是明确go返回值的方式跟C是不一样的,为了支持多值返回,go是用栈返回值的,而C是用寄存器。


    最最最重要的一点就是:return xxx 这一句语句并不是一条原子指令!

    整个return过程,没有defer之前是,先把在栈中写一个值,这个值被会当作返回值。然后再调用RET指令返回。return xxx语句汇编后是先给返回值赋值,再做一个空的return: ( 赋值指令 + RET指令)

    defer的执行是被插入到return指令之前的

    有了defer之后,就变成了 (赋值指令 + CALL defer指令 + RET指令)

    而在CALL defer函数中,有可能将最终的返回值改写了...也有可能没改写。总之,如果改写了,那么看上去就像defer是在return xxx之后执行的~

    这是所有你所想不明白的defer故事发生的根源。


    上面的基础知识都有了,然后就可以来说说神奇的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


    懂了么?

    结论:defer确实是在return之前调用的。但表现形式上却可能不像。本质原因是return xxx语句并不是一条原子指令,defer被插入到了赋值 与 RET之前,因此可能有机会改变最终的返回值。

    当你觉得迷糊时,可以用我给的这套规则转一下代码。






  • 相关阅读:
    题解 [CF891C] Envy
    题解 [BZOJ4710] 分特产
    题解 [BZOJ2159] Crash的文明世界
    题解 [BZOJ4144] Petrol
    #leetcode刷题之路1-两数之和
    week 7 文件操作与模板
    coursera 北京大学 程序设计与算法 专项课程 STL week8 list
    coursera 北京大学 程序设计与算法 专项课程 完美覆盖
    JSTL标签库不起作用的解决方案 .(转)
    javax.servlet.jsp.PageContext.getELContext()Ljavax/el/ELContext解决办法(转)
  • 原文地址:https://www.cnblogs.com/msnsj/p/4242579.html
Copyright © 2011-2022 走看看