Go语言defer与return执行的先后顺序详解
作者:捶捶自己 发布时间:2024-05-22 10:29:43
先了解什么是defer
Go语言中的defer与return执行的先后顺序
Go语言的 defer 语句会将其后面跟随的语句进行延迟处理,在 defer 归属的函数即将返回时,将延迟处理的语句按 defer 的逆序进行执行.也就是说,先被 defer 的语句最后被执行,最后被 defer 的语句,最先被执行。(与栈的先入后出是一个道理,也可以将其理解为入栈和出栈)
举一个简单的例子
func main() {
a, b := 111, 333
defer fmt.Println("b= ", b)
fmt.Println("a= ", a)
}
打印结果:
a= 111
b= 333
可以看到虽然执行语句时b在前,但是输出结果为b在最后被输出。
defer 的用法
(简单讲解,细节请自行查阅资料)
一般用来释放资源或者读写操作,当处理业务或逻辑中涉及成对的操作是一件比较烦琐的事情,比如打开和关闭文件、接收请求和回复请求、加锁和解锁等。在这些操作中,最容易忽略的就是在每个函数退出处正确地释放和关闭资源。比如下面一个例子
func main(){
a := 1
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
fmt.Fprintln(out, a)
}
输出结果:
1
就可以在最后将结果打印到控制台中去,类似的用法如关闭数据库资源等等。如果这个例子太过于简单,那么来看下个例子。
var a bool = true
defer func() {
fmt.Println("1")
}()
if a == true {
fmt.Println("2")
return
}
defer func() {
fmt.Println("3")
}()
输出结果:
2
1
我们会发现defer语句也是需要被执行的,如果在defer函数执行之前就执行return。defer后的语句就不会再被执行了。但是如果是在return之前defer已经执行,则defer中的语句将会在return执行之前先一步进行执行.
那么defer 和 return有什么联系?
defer 是延迟执行语句,return是返回语句,那么肯定出现谁先谁后的问题。下面看一个经典的例子吧
func increaseA() int {
var i int
defer func() {
i++
}()
return i
}
func increaseB() (r int) {
defer func() {
r++
}()
return r
}
func main() {
fmt.Println(increaseA())
fmt.Println(increaseB())
}
输出结果为:
0
1
肯定有人觉得有疑惑,为什么A函数没有输出,B函数却输出了呢?为什么答案不是1和0呢?
原因:
先说结论:defer 修饰的匿名函数,只能更新具名返回值.那么这会导致什么问题呢?我们来逐步分析这个例子。
在increaseA()函数中有一个声明i,表示i在该函数内已经被生成,是有名称的变量。但该函数返回参数为匿名参数.
在increaseB()函数中没有声明r,是匿名变量。但该函数返回参数为具名参数.
func increaseA() int,返回值i=0的时候该值已经被绑定到返回值里了,defer再去改i已经没用了.
func increaseB() (r int), 返回值r先把返回变量设为0,defer又把r改为1.这时候还能生效. 因此答案很明显为 1 和 0.
更进一步理解
我们若想要进一步理解也可以去输出汇编语句,然后进行研读,可惜我是个菜鸟读不懂汇编语言!但我们可以从return入手
我们要理解return 返回值的运行机制:
return
并非原子操作,分为赋值,和返回值两步操作.实际上return
执行了两步操作,因为返回值没有命名,所以return
默认指定了一个返回值(假设为a),首先将i赋值给a,后续的操作因为是针对i进行的,所以不会影响a, 此后因为a不会更新,所以return a
不会改变.
var i int
a := i
return a
但是如果return的参数a是具名参数,就像上述例子中increaseB()函数一样。a就相当于命名的变量i, 因为所有的操作都是基于命名变量i(a),返回值也是i, 所以每一次defer操作,都会更新返回值i.
省流小结
return会将返回值先保存起来,对于无名返回值来说,保存在一个临时对象中,defer是看不到这个临时对象的;而对于有名返回值来说,就保存在已命名的变量中。
来源:https://juejin.cn/post/7171066100052918308


猜你喜欢
- 这篇文章主要介绍了python matplotlib饼状图参数及用法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考
- 本文实例讲述了Python实现批量读取word中表格信息的方法。分享给大家供大家参考。具体如下:单位收集了很多word格式的调查表,领导需要
- 有一个古老的争论,是关于在哪里存储应用程序业务逻辑的:是在应用程序本身的业务逻辑层中还是在数据库层中。应用程序逻辑层的绝对支持者提出,数据库
- 数值运算代码:# -*- coding=GBK -*-import cv2 as cv# 数值运算:加减乘除def shu_image(sr
- 简介对与控件QPushButton中的可以使用setStyleSheet设置它背景图片。具体设置背景图片的方法有两种self.button.
- <?php /********************************************** *&n
- 看代码吧~name = input('Name') height = input('Height(m):')
- 春节休息了几天,今天上班第一天,最近混twitter混得比较多,经常要压缩URL,以前做了个书签用http://is.gd/压缩,后来发现了
- InnoDB和MyISAM是在使用MySQL最常用的两个表类型,各有优缺点,视具体应用而定。下面是已知的两者之间的差别,仅供参考。1.Inn
- Character 字符串:数据类型描述存储char(n)固定长度的字符串。最多 8,000 个字符。nvarchar(n)可变长度的字符串
- python中return的用法1、return语句就是把执行结果返回到调用的地方,并把程序的控制权一起返回程序运行到所遇到的第一个retu
- 在我发表上一篇《Zen Coding: 一种快速编写HTML/CSS代码的方法》之后,有网友表示不知道怎么在Dreamweaver上使用ze
- 使用[[v]*n]*n遇到的坑今天通过[[v]*n]*n快速创建列表, 当我使用索引修改值时, 本来是打算修改a[0][0]这一个元素的第一
- 数据可视化是数据科学或机器学习项目中十分重要的一环。通常,你需要在项目初期进行探索性的数据分析(EDA),从而对数据有一定的了解,而且创建可
- xml即可扩展标记语言,它可以用来标记数据、定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言。从结构上,很像HTML超文本标记语
- 怎样才能将在表A取得的数据插入另一个表B中?(1)对于表A和表B两个表结构完全相同的话〔字段个数,相应字段的类型等等〕,可以使用 inser
- 1、PHP加密解密PHP加密和解密函数可以用来加密一些有用的字符串存放在数据库里,并且通过可逆解密字符串,该函数使用了base64和MD5加
- 本文实例讲述了python实现数值积分的Simpson方法。分享给大家供大家参考。具体如下:#coding = utf-8#simpson
- 在本身比较复杂的页面里,再突出信息,往往是把几种方法叠加起来使用,比如加粗加大、加粗加色等,区别在于使用的类别和程度。导致的结果是呈现越来越
- 如今各个框架都在模块化,连前端的javascript也不例外。每个模块负责一定的功能,模块与模块之间又有相互依赖,那么问题来了:javasc