文档章节

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

鼎铭
 鼎铭
发布于 2018/06/18 17:29
字数 788
阅读 265
收藏 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 的返回值问题不会再错了。

 

© 著作权归作者所有

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

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

blacklovebear
2014/02/09
0
0
Golang中多用途的defer

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

gotaly
2018/06/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Python如何开发桌面应用程序?Python基础教程,第十三讲,图形界面

当使用桌面应用程序的时候,有没有那么一瞬间,想学习一下桌面应用程序开发?行业内专业的桌面应用程序开发一般是C++,C#来做,Java开发的也有,但是比较少。本节课会介绍Python的GUI(图形用...

程序员补给栈
40分钟前
3
0
kafka在的使用

一、基本概念 介绍 Kafka是一个分布式的、可分区的、可复制的消息系统。它提供了普通消息系统的功能,但具有自己独特的设计。 这个独特的设计是什么样的呢? 首先让我们看几个基本的消息系统...

狼王黄师傅
47分钟前
1
0
Android JNI总结

0x01 JNI介绍 JNI是Java Native Interface的缩写,JNI不是Android专有的东西,它是从Java继承而来,但是在Android中,JNI的作用和重要性大大增强。 JNI在Android中起着连接Java和C/C++层的作...

天王盖地虎626
昨天
1
0
大数据教程(11.8)Hive1.2.2简介&初体验

上一篇文章分析了Hive1.2.2的安装,本节博主将分享Hive的体验&Hive服务端和客户端的使用方法。 一、Hive与hadoop直接的关系 Hive利用HDFS存储数据,利用MapReduce查询数据。 二、Hive与传统数...

em_aaron
昨天
3
0
跟我学Spring Cloud(Finchley版)-15-Hystrix监控详解

Hystrix提供了监控Hystrix Command的能力,本节来详细探讨。 监控端点与数据 应用整合Hystrix,同时应用包含spring-boot-starter-actuator 依赖,就会存在一个/actuator/hystrix.stream 端点...

周立_ITMuch
昨天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部