Go 中 defer 和 return 执行的先后顺序

  1. 多个defer的执行顺序为“后进先出”;

  2. defer、return、返回值三者的执行逻辑应该是:return最先执行,return负责将结果写入返回值中;接着defer开始执行一些收尾工作;最后函数携带当前返回值退出。

如果函数的返回值是无名的(不带命名返回值),则go语言会在执行return的时候会执行一个类似创建一个临时变量作为保存return值的动作,而有名返回值的函数,由于返回值在函数定义的时候已经将该变量进行定义,在执行return的时候会先执行返回值保存操作,而后续的defer函数会改变这个返回值(虽然defer是在return之后执行的,但是由于使用的函数定义的变量,所以执行defer操作后对该变量的修改会影响到return的值

eg1:不带命名返回值的函数

package main

import "fmt"

func main() {
fmt.Println("return:", test())// defer 和 return之间的顺序是先返回值, i=0,后defer
} func test() int {//这里返回值没有命名
var i int
defer func() {
i++
fmt.Println("defer1", i) //作为闭包引用的话,则会在defer函数执行时根据整个上下文确定当前的值。i=2
}()
defer func() {
i++
fmt.Println("defer2", i) //作为闭包引用的话,则会在defer函数执行时根据整个上下文确定当前的值。i=1
}()
return i
} 

test() 先返回 i=0

defer2先于defer1执行

输出结果为:

defer2 1

defer1 2

return: 0

eg2:带命名返回值的函数:

package main

import "fmt"

func main() {
fmt.Println("return:", test())
} func test() (i int) { //返回值命名i
defer func() {
i++
fmt.Println("defer1", i)
}()
defer func() {
i++
fmt.Println("defer2", i)
}()
return i
}
对外部变量的引用作为函数参数(i),则在defer申明时就把值传递给defer,

 

 输出结果为:

defer2 1

defer1 2

return: 2

理解return 返回值的运行机制:

为了弄清上述两种情况的区别,我们首先要理解return 返回值的运行机制:
return 并非原子操作,分为赋值,和返回值两步操作
eg1 : 实际上return 执行了两步操作,因为返回值没有命名,所以
return 默认指定了一个返回值(假设为s),首先将i赋值给s,后续
的操作因为是针对i,进行的,所以不会影响s, 此后因为s不会更新,所以
return s 不会改变
相当于:
var i int
s := i
return s
eg2 : 同上,s 就相当于 命名的变量i, 因为所有的操作都是基于
命名变量i(s),返回值也是i, 所以每一次defer操作,都会更新
返回值i

 

最新文章

  1. 记录一次bug解决过程:git深入学习和JDK8新特性
  2. qt之串口
  3. hive日期函数
  4. yii中sphinx索引配置解析
  5. javascript注意点(1)
  6. android webview内容压线问题解决方法
  7. IIS7.0/7.5 MVC3 实现伪静态
  8. ASP.NET 5- 1
  9. SmartSql Config配置
  10. 如何让input框显示在一行?
  11. JavaScript 里,$ 代表什么?/JQuery是什么语言?/html中用link标签引入css时的中 rel="stylesheet"属性?/EL表达式是什么?
  12. be动词的形式和用法
  13. 【iCore1S 双核心板_FPGA】例程十一:Modelsim仿真实验
  14. 【设计模式】—— 解释器模式Interpret
  15. scala 资料集结
  16. virtio-netdev 数据包的发送
  17. Web Api问题汇总
  18. [LeetCode]206. Reverse Linked List(链表反转)
  19. 简析 Tomcat 、Nginx 与 Apache 的区别
  20. centos安装GD库失败

热门文章

  1. 【技术博客】 利用Postman和Jmeter进行接口性能测试
  2. Spring Cloud Ribbon 源码分析---负载均衡算法
  3. android中SELINUX规则分析和语法简介
  4. RocketMQ常用命令【转】
  5. Mysql 查询今天,这周,这个月,今年的数据
  6. RedHat 7关闭防火墙方法
  7. Anaconda(三)
  8. resources-plugin-2.6.pom.part.lock (没有那个文件或目录)
  9. UUIDGenerator
  10. vue脚手架中使用Vant,实现自动按需引入组件,并将px转换为rem