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

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

 

原文地址:https://www.cnblogs.com/saryli/p/11371912.html

时间: 2024-10-13 23:45:40

Go ---- defer 和 return 执行的先后顺序的相关文章

go中defer的理解--defer、return、返回值之间执行顺序

defer可以读取有名返回值 func c() (i int) { defer func() { i++ }() return 1 } 输出结果是2. 在开头的时候,我们知道defer是在return调用之后才执行的. 这里需要明确的是defer代码块的作用域仍然在函数之内,结合上面的函数也就是说,defer的作用域仍然在c函数之内.因此defer仍然可以读取c函数内的变量(如果无法读取函数内变量,那又如何进行变量清除呢....). 当执行return 1 之后,i的值就是1. 此时此刻,def

finally return 执行顺序问题

网上有很多人探讨Java中异常捕获机制try...catch...finally块中的finally语句是不是一定会被执行?很多人都说不是,当然他们的回答是正确的,经过我试验,至少有两种情况下finally语句是不会被执行的: (1)try语句没有被执行到,如在try语句之前就返回了,这样finally语句就不会执行,这也说明了finally语句被执行的必要而非充分条件是:相应的try语句一定被执行到. (2)在try块中有System.exit(0);这样的语句,System.exit(0);

【原】不定义Order属性,通过切面类的定义顺序来决定通知执行的先后顺序

[结论] 若不同切面类执行时,在没有定义“order属性”,而且切面类中触发增强通知的切入点都相同,则在切面类中的通知的执行顺序与该切面类在<aop:config>元素中“声明的顺序”相关,即先声明的切面类先执行,后声明的切面类后执行. [代码示例] 1 <aop:config> 2 <!-- 用户自定义的切面01,用于不同切面类执行顺序的测试 --> 3 <aop:aspect id="myMethod01Aspect" ref="

Unity3D 中脚本执行的先后顺序

Unity3D本身自带有控制脚本执行先后顺序的方法: Edit ---> Project Settings ---> Script Execution Order  ---> 值越小脚本会越先被执行 Unity3D 中脚本执行的先后顺序,码迷,mamicode.com

【原】Order属性决定了不同切面类中通知执行的先后顺序

[障碍再现] MyBatis配置多数据源时,数据源切换失败. [原因分析]    自定义切面和Spring自带事务切面“即<aop:advisor>”执行的先后顺序导致数据源不能切换成功. [解决方案]1.配置代码 1 <aop:config> 2 <!-- 1.Spring框架自身提供的切面 --> 3 <aop:advisor advice-ref="userTxAdvice" pointcut="execution(public

ASP.NET中HttpApplication中ProcessRequest方法中执行的事件顺序;ASP.NET WebForm和MVC整体请求流程图

ASP.NET中HttpApplication中ProcessRequest方法中执行的事件顺序 1.BeginRequest  开始处理请求 2.AuthenticateRequest 授权验证请求,获取用户授权信息 3.PostAuthenticateRequest 获取成功 4.AunthorizeRequest 授权,一般来检查用户是否获得权限 5.PostAuthorizeRequest 获得授权 6.ResolveRequestCache 获取页面缓存结果(如果没有则执行) 7.Po

jsp页面中的代码执行加载顺序介绍(转)

原文出处:http://www.3lian.com/edu/2014/01-10/123053.html 本篇文章主要是对jsp页面中的代码执行加载顺序进行了详细的介绍,需要的朋友可以过来参考下,希望对大家有所帮助 1. java是在服务器端运行的代码,jsp在服务器的servlet里运行,而javascript和html都是在浏览器端运行的代码.所以加载执行顺序是是java>jsp>js. 2. js在jsp中的加载顺序 页面上的js代码时html代码的一部分,所以页面加载的时候是由上而下加

try,catch,finally和return执行顺序的知识点

   今天java学到try,catch,finally以及return的使用,刚开始模糊不清,后来也差不多清楚了,分享给大家,有什么问题谢谢指出.         try-catch是java中用来捕获异常的方法,也就是帮我们找到代码中存在的异常,其格式如下:   try{   代码段1      }   catch{   代码段2      }   代码段1是一段存在异常的代码,代码段2是处理代码段1异常的一些方法,当然可以有多个catch,表示同时提供多个处理代码段1异常的方法.(当代码段

Ember.js 入门指南——番外篇,路由、模板的执行、渲染顺序

在Ember中路由和模板的执行都是有一定顺序的,它们的顺序为:主路由à子路由1à子路由2à子路由3à--.模板渲染的顺序与路由执行顺序刚好相反,从最后一个模板开始解析渲染.        注意:模板的渲染是在所有路由执行完之后,从最后一个模板开始.关于这一点下面的代码会演示验证,官网教程有介绍,点击查看. 比如有一路由格式为"application/posts/detail/comments/comment",此时路由执行的顺序为:application/postsàdetailàco