文档章节

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

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

    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 的返回值问题不会再错了。

 

© 著作权归作者所有

共有 人打赏支持
鼎铭
粉丝 28
博文 67
码字总数 40583
作品 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
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
Go的异常处理 defer, panic, recover

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

blacklovebear
2014/02/09
0
0

没有更多内容

加载失败,请刷新页面

加载更多

linux运维人员必会运维工具

linux运维人员必会开源运维工具体系 说明:不同的技术人员,不同的阶段确定知识边界非常重要,否则,就像马拉车,不知道终点在哪,累死也达不到目标。例如拿8K要学多少,拿15K要学多少。一个...

寰宇01
20分钟前
2
0
10大PHP比特币开源项目

如果你是一个Phper,如果你希望学习区块链,那么本文列出的 10个开源的Php比特币项目,将有助于你了解在自己的应用中 如何加入对比特币的支持。 如果你希望快速掌握使用Php对接比特币钱包的方...

汇智网教程
41分钟前
1
0
springclould feign客户端添加全局参数

用springclould feign作为调用服务的客户端,一般来说参数可以写在feignclient的方法参数里 有时需要所有feign请求都统一添加一些参数,例如token用于鉴权等,可以这样做: 添加一个配置类,...

canneljls
42分钟前
1
0
win32截屏并rgb24转yuv420

//最终f的内存布局为BGRA格式,需要保证buf长度足够(>w*h*4)void ScreenCap(void* buf, int w, int h){ HWND hDesk = GetDesktopWindow(); HDC hScreen = GetDC(hDesk); ......

styleman
今天
1
0
php输出mysql取出的中文为??的问题

解决方法: @ $db=new mysqli(DB_HOST,DB_USER,DB_PASSWORD,DB_DB); $db->query("set names utf8");//添加此语句,可以解决问题...

Aomo
今天
1
2

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部