什么是defer?

defer语句是专门在函数结束以后做一些清理工作的。我们先举一个例子来更好的理解,现在有一个函数,它的作用是把一个文件内容拷贝到另一个文件。

func CopyFile(dstName string, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
} dst, err := os.Create(dstName)
if err != nil {
return
} written, err = io.Copy(dst, src)
src.Close()
dst.Close()
return
}

以上代码是可以正常执行的,但是存在一个问题,如果os.Create执行失败,那么就无法执行到文件资源的Close函数。进程每打开一个文件就会占用一个文件描述符,而在系统当中,文件描述符是有上限的,可以通过ulimit -n查看,如果资源没有被及时释放,会出现资源浪费的情况。如果打开文件过多,也会出现Too many open files的提示。这个时候就需要通过defer来解决问题了,代码如下。

func CopyFile(dstName string, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close() dst, err := os.Create(dstName)
if err != nil {
return
}
defer dst.Close() written, err = io.Copy(dst, src)
return
}

defer语句会在return参数设置之后、函数返回给调用者之前执行,这样就不再担心文件资源无法被Close了。

defer的三个规则

规则一:被deferred的函数参数在defer时确定

func a() {
i := 0
defer fmt.Println(i)
i++
return
}

我们通过以上代码来理解这个拗口的规则,如果根据官方的定义来理解这段代码,变量i的值铁定为1,但实际执行的结果不是1,却是0。

Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked. Instead, deferred functions are invoked immediately before the surrounding function returns, in the reverse order they were deferred.

那我们再重新来理解这个规则,变量i是在逐行执行到defer语句的时候就已经确定了值,这个时候变量i还没有进行自增,所以输出的结果应该是0而不是1。

规则二:被deferred函数执行顺序遵循LIFO原则

func b() {
for i := 0; i < 4; i++ {
defer fmt.Print(i)
}
}

LIFO全称为Last In First Out,意为后进先出,栈是一种典型的LIFO数据结构。defer也是如此,拿以上代码为例,先后遍历了四次,也就是做了四次压栈操作。同理,在函数return之前,就会逐一出栈,倒序执行defer语句,所以上述代码输出内容为3210。

规则三:deferred函数可以读取和修改函数的返回值

func c() (i int) {
defer func() { i++ }()
return 1
}

我们定义一个defer函数,将变量i自增,那么最终变量i的值为2。我们从官方文档中可以看出,defer语句是在函数设置返回值后,且在返回给主调函数前执行的,根据这个思路,c函数已经return了1,这个时候执行了defer函数,将返回的结果1进行了自增,然后返回给主调函数,这个时候主调函数拿到的值就是2了。

deferred functions are executed after any result parameters are set by that return statement but before the function returns to its caller

Go语言defer分析的更多相关文章

  1. R语言︱情感分析—词典型代码实践(最基础)(一)

    每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- 笔者寄语:词典型情感分析对词典要求极高,词典中 ...

  2. 【转】对 Rust 语言的分析

    对 Rust 语言的分析 Rust 是一门最近比较热的语言,有很多人问过我对 Rust 的看法.由于我本人是一个语言专家,实现过几乎所有的语言特性,所以我不认为任何一种语言是新的.任何“新语言”对我来 ...

  3. C语言内存分析

    C语言内存分析 一.进制 概念:进制是一种计数方式,是数值的表现形式 4种主要的进制: ①. 十进制:0~9 ②. 二进制:0和1 ③. 八进制:0~7 ④. 十六进制:0~9+a b c d e f ...

  4. go语言 defer 高级

    go语言defer语句的用法 defer的语法 defer后面必须是函数调用语句,不能是其他语句,否则编译器会出错. package main import "log" func ...

  5. go语言---defer

    go语言---defer https://blog.csdn.net/cyk2396/article/details/78885135 defer 是在函数退出前调用,多个defer遵循 先进后出 的 ...

  6. 深入 Go 语言 defer 实现原理

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客: https://www.luozhiyun.com/archives/523 本文使用的go的源码 1.15.7 介绍 defer 执行规 ...

  7. go语言defer使用

    defer Go语言中有种不错的设计,即延迟(defer)语句,你可以在函数中添加多个defer语句.当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回.特别是当你在进行一些打开资源 ...

  8. R语言︱情感分析—基于监督算法R语言实现(二)

    每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- 笔者寄语:本文大多内容来自未出版的<数据 ...

  9. go语言 defer 你不知道的秘密!

    go 语言的defer功能强大,对于资源管理非常方便,但是如果没用好,也会有陷阱哦.我们先来看几个例子. 例一: defer 是先进后出 这个很自然,后面的语句会依赖前面的资源,因此如果先前面的资源先 ...

随机推荐

  1. 前端页面设计常见的30个CSS选择器

    1. *   -->   通配符选择器 * { margin: 0; padding: 0; } 星号符会选择页面每个元素.很多开发者用它把所有margin和padding归零.这当然是快捷测试 ...

  2. ConxtMenu高级用法

    ##背景我们经常在列表的页面中,点击列表中的行,一般进入详情页面,长按列表中一行,会弹出一个菜单,包含了对某一行的操作(编辑.删除等等),也知道通常的用法: 0x01. 在Activity中注册需要上 ...

  3. 【NOIP14 D2T2】寻找道路

    Source and Judge NOIP2014 提高组 D2T2Luogu2296Caioj1567 Problem [Description] 在有向图 G 中,每条边的长度均为 1,现给定起点 ...

  4. 二手iPhone,为什么没有火?

    据相关媒体报道,日前苹果准备向印度市场销售二手iPhone,以提高自己在这个市场内的竞争力,但却遭遇到了强烈的反对,首先是印度本土手机制造商担心二手iPhone会冲击他们的销售,让其本就微薄的利润更加 ...

  5. 从iPhone下滑 看科技转型之困

    看科技转型之困" title="从iPhone下滑 看科技转型之困"> 毫无疑问,苹果正在面临一次关键转型.最近苹果股价较高点134.54美元下跌21%以上的实事, ...

  6. 【原创】从零开始搭建Electron+Vue+Webpack项目框架(五)预加载和Electron自动更新

    导航: (一)Electron跑起来(二)从零搭建Vue全家桶+webpack项目框架(三)Electron+Vue+Webpack,联合调试整个项目(四)Electron配置润色(五)预加载及自动更 ...

  7. Apple App签名机制

    概览 数字签名 签名机制与验证过程 操作流程 数字签名 摘要算法 将任意长度文本通过一个算法得到一个固定长度的文本. 源文本不同,计算结果必然不同 无法从结果反推源 例如,MD5和SHA算法 非对称加 ...

  8. Git 相关问题分享,git reset与git revert的区别?

    1.如果我在git add 后想要撤销操作,该怎么做? 使用 git rm --cache [文件名/ *] 或者 git reset HEAD, 为什么这个命令也会有效果呢,实际上reset将 HE ...

  9. 【Amaple教程】2. 模块

    正如它的名字,模块用于amaplejs单页应用的页面分割,所有的跳转更新和代码编写都是以模块为单位的. 定义一个模块 一个模块由<module>标签对包含,内部分为template模板.J ...

  10. Java基础--方法的定义

    1.为什么要有方法? 方法(又叫函数)就是一段特定功能的代码块.方法提高程序的复用性和可读性. 比如,有了方法,我们可以把要重复使用的一段代码提炼出来,然后在每个需要执行这段代码的地方去调用即可. 2 ...