文档章节

从golang函数栈空间分布看defer,你就不会再错了

鼎铭
 鼎铭
发布于 06/18 17:29
字数 788
阅读 240
收藏 6

    defer 是golang 面试常会面的一个点,但是实在话, 这玩意没多大用,特别是高频下,很多厂的优化点之一就是defer。但是这玩意复杂起来,你确实不一定能都答对,到底怎么分析defer ,才能保证返回值正常呢?其实明白 golang 的函数栈空间布局,就不会再弄错了。

    参考网上一哥们的文章,http://www.zenlife.tk/golang-defer.md,这个兄弟拿了三个例子,总结了一个方法,对于处理带复杂返回值的情况是有用的。

    首先做个测试题,如果全部都能做对,这篇文章就没必要看了,要是感觉有点瞎蒙,就还是看下:

ex1:

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

ex2:

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

ex3:

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

    这三个例子基本涵盖了defer 最复杂的情况,而且非常有代表性。

   那个兄弟说的比较清楚了,他也总结了一个很好的方法,这里我不复述他说的内容,谈下自己的理解,他的方法是这样的,当出现defer 的时候,我们拆解成下面步骤:

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

    为什么这样是没问题的,有两个关键点,第一个,golang 的返回值是通过栈空间,不是通过寄存器,这点最重要。调用函数前,首先分配的是返回值空间,然后是入参地址,再是其他临时变量地址。第二点,return 是非原子的,return 操作的确是分三步,将返回值拷贝到栈空间第一块区域,然后再执行defer 操作,最后一个ret 跳转,这个操作的确是可以对应到汇编代码的。然后,这里第二步很巧妙,这里的返回值是否在定义的时候已经命名了?defer 是否能更改栈空间第一块区域的地址的值(是否在defer作用域)?这里画画图立马就能看明白。。。

    看ex1,函数栈空间如下图,这里没有入参,返回区域有名 result, result 在defer 的作用域,执行defer 的过程修改了result 的值,直接修改了函数返回值栈空间的值。所有,ex1的结果是1。

    再看ex2,函数的栈空间如下:

    

    注意下执行过程,这里的返回值地址r,根本不在defer 的作用域,defer 修改不了r的值,return t = 5 的时候,实际是 第一步:t = 5, 第二步,r = t, 第三步:defer 函数,第四部:ret 。从第四步的时候就不再修改r 的值了。

    最后看ex3就简单了,同样的方法,第一步 r = 1 ,返回值是有名的,这时,defer 入参是r 并不是r 地址,并不能修改r ,所以最后return 的值是1。

    上面的例子明白了,明白了函数的栈区分布基本defer 的返回值问题不会再错了。

 

© 著作权归作者所有

共有 人打赏支持
鼎铭
粉丝 47
博文 68
码字总数 42542
作品 0
东城
程序员
私信 提问
Go圣经-学习笔记之defer和异常处理

上一篇 Go圣经-学习笔记之函数值(二) 下一篇 Go圣经-学习笔记之方法 可变参数 形参数量可变的函数称为可变参数函数。使用最多的可变参数函数标准库:。 在声明可变参数函数时,需要在参数列表...

cdh0805010
2017/10/25
0
0
golang入门学习笔记(四)

作者: 一字马胡 转载标志 【2017-11-25】 更新日志 日期 更新内容 备注 2017-11-25 新建文章 go语言入门学习笔记(四) golang入门学习笔记系列 golang入门学习笔记(一) golang入门学习笔...

一字马胡
2017/11/25
0
0
Go的异常处理 defer, panic, recover

Go语言追求简洁优雅,所以,Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱。因为开发者很容易滥用异常,...

blacklovebear
2014/02/09
0
0
golang捕获异常

Go语言追求简洁优雅,所以,Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱。因为开发者很容易滥用异常,...

吴之恒心
2017/03/01
0
0
Golang中多用途的defer

defer顾名思义就是延迟执行,那么defer在Golang中该如何使用以及何时使用呢? A "defer" statement invokes a function whose executionis deferred to the moment the surrounding function......

gotaly
06/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

纹理与表面细节添加方法---凹凸映射

中国龙-扬科
20分钟前
0
0
20181115上课截图

小丑鱼00
28分钟前
0
0
初识css自定义属性

这算是一篇两篇文章译文的糅合体,旨在帮助理解css自定义属性。 今天,CSS预处理器是Web开发的标准。 预处理器的一个主要优点是它们使您能够使用变量, 这有助于您避免复制和粘贴代码,并简化...

嫣然丫丫丫
37分钟前
1
0
JAVA基础--session共享的前生今世

session共享的前生今世 Session及cookie基本概念及生命周期 session   当浏览器发起一个新的HTTP请求时,WEB服务端会主动创建一个session.并分配一个sessionID作为服务端识别客户端的一个标...

spinachgit
46分钟前
0
0
Deepin Linux 下把 UC 缓存视频变为 MP4 文件

本文是利用 FFMPEG 的功能实现的。 生成 file.txt文件 因为缓存文件都是数字,且文件夹内还有其他文件,包括 index.* 的文件。 $ ls -1v --hide=file.txt --hide=index* > file.txt 解释 ls...

不避风云
47分钟前
0
2

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部