当前位置: 首页 > news >正文

Golang中defer和return的执行顺序 + 相关测试题(面试常考)

参考文章:

  • 【Golang】defer陷阱和执行原理
  • GO语言defer和return 的执行顺序
  • 深入理解Golang defer机制,直通面试

面试富途的时候,遇到了1.2的这个进阶问题,没回答出来。这种题简直是 噩梦\color{purple}{噩梦}噩梦,很久不关注基本上就忘记了…


一、defer相关测试题

1.1 关于 defer 函数后的 匿名/有名 返回值对输出结果的影响:

package mainimport ("fmt"
)func f1() int{var i intdefer func(){i++}()return i    
}func f2() (i int){defer func(){i++}()return i    
}func main() {fmt.Println(f1())fmt.Println(f2())
}

上面的程序输出结果是啥呢?答案是:0 1

我们知道Golang中的函数返回值有匿名和有名两种

  • 对于匿名的可以理解成执行return 语句时,分成两步,第一步需要设置一个临时变量s用来接收返回值,第二步将临时变量s返回。
  • 对于有名的可以理解成执行return的时候,直接将变量返回。

而我们知道,所有的defer都将在真正的 return 变量之前运行,所以对于上面两种情况,defer对于返回值的影响也有两种:

  • 对于匿名的:第一步设置临时变量保存返回值;第二步按照defer的执行步骤执行defer语句,如果其中有对变量的修改,将不会影响s变量的值。
  • 对于有名的:第一步先执行defer,对变量进行修改;第二步,返回被修改的返回值。

所以,理解了上面的步骤的之后,我们就可以理解 f1()f2() 这两个的函数了,在执行 return 时:

  • f1函数先通过临时变量s保存i的值,此时i为0,所以s=0,然后执行defer,修改i的值,i变为1,最后返回s的值,因为s=0,所以返回值是0;
  • 而f2是有名函数,所以执行return 的时候,i被设置为1,然后真正的返回i值,所以返回的是1。

1.2 关于defer的进阶测试题

再来看一段示例代码:

可以先尝试想想以下的几个函数分别会输出什么内容?

func test1() (x int) {defer fmt.Printf("in defer: x = %d\n", x)x = 7return 9
}func test2() (x int) {x = 7defer fmt.Printf("in defer: x = %d\n", x)return 9
}func test3() (x int) {defer func() {fmt.Printf("in defer: x = %d\n", x)}()x = 7return 9
}func test4() (x int) {defer func(n int) {fmt.Printf("in defer x as parameter: x = %d\n", n)fmt.Printf("in defer x after return: x = %d\n", x)}(x)x = 7return 9
}func main() {fmt.Println("test1")fmt.Printf("in main: x = %d\n", test1())fmt.Println("test2")fmt.Printf("in main: x = %d\n", test2())fmt.Println("test3")fmt.Printf("in main: x = %d\n", test3())fmt.Println("test4")fmt.Printf("in main: x = %d\n", test4())
}

你已经计算出结果了吗?看看和运行结果是不是一样的,如果不一样继续阅读本文吧:

test1
in defer: x = 0
in main: x = 9
test2
in defer: x = 7
in main: x = 9
test3
in defer: x = 9
in main: x = 9
test4
in defer x as parameter: x = 0
in defer x after return: x = 9
in main: x = 9

进阶测试题 解析

在看解析之前,先理解下 return 语句的执行顺序:
return语句本身并不是一条原子指令,它会先给返回值赋值,然后再是真正的返回结果到函数外部,如下:

func f() (r int) {return 1
}//执行过程:
r:=1 //赋值
ret  //执行返回

而在含defer表达式时,函数返回的过程是这样的:
先给返回值赋值,然后调用defer表达式,最后再是返回结果

这4个测试函数中,都是return 9并且没有对返回值进行修改,所以主函数main()中的输出都是:in main: x = 9,我相信这个大家应该是没有疑问的。接下来看每个测试函数defer的打印。

test1:defer执行时,对Printf的入参x进行计算,它的值是0,并且传递给函数,return 9后执行Printf,所以结果是in defer: x = 0。

func test1() (x int) {// defer后接【表达式】:// 起初x作为入参值为0,结果一直保留在栈中,最后直接打印输出,不受后续影响defer fmt.Printf("in defer: x = %d\n", x)x = 7// 返回值为 9return 9
}

test2:与test1类似,不同的是,defer执行是在x=7之后,所以x的值是7,并且传递给Printf,所以结果是:in defer: x = 7。

func test2() (x int) {x = 7// defer后接【表达式】:// 起初x作为入参值为7,结果一直保留在栈中,最后直接打印输出,不受后续影响defer fmt.Printf("in defer: x = %d\n", x)// 返回值为 9return 9
}

test3defer后跟的是一个匿名函数,匿名函数能访问外部函数的变量,这里访问的是test3的x,defer执行时,匿名函数没有入参,所以把func()()压入到栈,return语句之后,执行func()(),此时匿名函数获得x的值是9,所以结果是in defer: x = 9。

func test3() (x int) {// defer后接【匿名函数】:defer func() {// 匿名函数能访问外部函数的变量,受后续return x的结果影响// 也就是说【defer匿名函数内要访问的变量 x】最终会被【defer匿名函数外最终要 return 返回出去的变量 x】所影响fmt.Printf("in defer: x = %d\n", x)}()x = 7// 返回值为 9return 9
}

test4:与test3的不同是,匿名函数有一个入参n,我们把x作为入参打印,还有就是匿名函数中的x能访问外部函数的变量x。defer执行时,x=0,所以入栈的函数是func(int)(0),return语句之后执行func(int)(0),即n=0,x在匿名函数内没有定义,依然访问test4中的x,此时x=9,所以结果为:in defer x as parameter: x = 0, in defer x after return: x = 9。

func test4() (x int) {// defer后接【匿名函数】:defer func(n int) {// 针对【defer匿名函数内要访问的变量 n】,其值取决于在一开始遇到 defer 时入参 n 的值,// 也就是最开始还没被改变时的变量 x 的值,将其赋值给入参 n,起初是0// 这个变量 n 的值是独立的,不会受后续 return x 结果的影响。fmt.Printf("in defer x as parameter: x = %d\n", n)fmt.Printf("in defer x after return: x = %d\n", x)}(x)x = 7return 9
}

小结:

  • 首先要明确 defer 后紧接的代码可以有两种写法:
    • defer + 表达式,例如:defer fmt.Printf("in defer: x = %d\n", x)
      此时就会直接保留当前变量 x 已有的值,一直到最后直接打印输出,不再受后续 return x 结果的影响。例如test1和test2中的变量x。\color{red}{例如 test1 和 test2 中的变量 x。}例如test1test2中的变量x
    • defer + 匿名函数(无入参/有入参),例如:test3中的 defer func() {fmt.Printf("in defer: x = %d\n", x)}()或 test4中 defer func(n int) {fmt.Printf("in defer x as parameter: x = %d\n", n) fmt.Printf("in defer x after return: x = %d\n", x)}(x)
      此时需要区分打印输出的变量到底是【defer匿名函数内要访问的变量 n】,还是【defer匿名函数内要访问的变量 x】。
      • 针对【defer匿名函数内要访问的变量 n】,其值取决于在一开始遇到 defer 时入参 n 的值(也就是最开始还没被改变时的变量 x 的值,起初是0)。这个变量 n 的值是独立的,不会受后续 return x 结果的影响。例如test4中的变量n。\color{red}{例如 test4 中的变量 n。}例如test4中的变量n
      • 针对【defer匿名函数内要访问的变量 x】,由于匿名函数能访问外部函数的变量,也就是说【defer匿名函数内要访问的变量 x】最终会被【defer匿名函数外最终要 return 返回出去的变量 x】所影响。例如test3和test4中的变量x。\color{red}{例如 test3 和 test4 中的变量 x。}例如test3test4中的变量x

相关文章:

Golang中defer和return的执行顺序 + 相关测试题(面试常考)

参考文章: 【Golang】defer陷阱和执行原理 GO语言defer和return 的执行顺序 深入理解Golang defer机制,直通面试 面试富途的时候,遇到了1.2的这个进阶问题,没回答出来。这种题简直是 噩梦\color{purple}{噩梦}噩梦,…...

谁说菜鸟不会数据分析,不用Python,不用代码也轻松搞定

作为一个菜鸟,你可能觉得数据分析就是做表格的,或者觉得搞个报表很简单。实际上,当前有规模的公司任何一个岗位如果没有数据分析的思维和能力,都会被淘汰,数据驱动分析是解决日常问题的重点方式。很多时候,…...

php mysql保健品购物商城系统

目 录 1 绪论 1 1.1 开发背景 1 1.2 研究的目的和意义 1 1.3 研究现状 2 2 开发技术介绍 2 2.1 B/S体系结构 2 2.2 PHP技术 3 2.3 MYSQL数据库 4 2.4 Apache 服务器 5 2.5 WAMP 5 2.6 系统对软硬件要求 6 …...

Vue3电商项目实战-首页模块6【22-首页主体-补充-vue动画、23-首页主体-面板骨架效果、4-首页主体-组件数据懒加载、25-首页主体-热门品牌】

文章目录22-首页主体-补充-vue动画23-首页主体-面板骨架效果24-首页主体-组件数据懒加载25-首页主体-热门品牌22-首页主体-补充-vue动画 目标: 知道vue中如何使用动画,知道Transition组件使用。 当vue中,显示隐藏,创建移除&#x…...

linux 使用

一、操作系统命令 1、版本命令:lsb_release -a 2、内核命令:cat /proc/version 二、debian与CentOS区别 debian德班和CentOS是Linux里两个著名的版本。两者的包管理方式不同。 debian安装软件是用apt(apt-get install),而CentOS是用yum de…...

基于遗传算法的微电网调度(风、光、蓄电池、微型燃气轮机)(Matlab代码实现)

💥💥💥💞💞💞欢迎来到本博客❤️❤️❤️💥💥💥🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清…...

方向导数与梯度下降

文章目录方向角与方向余弦方向角方向余弦方向导数定义性质梯度下降梯度下降法(Gradient descent)是一个一阶最优化算法,通常也称为最速下降法。 要使用梯度下降法找到一个函数的局部极小值,必须向函数上当前点对应梯度&#xff08…...

Java岗面试题--Java基础(日积月累,每日三题)

目录面试题一:Java中有哪些容器(集合类)?追问:Java中的容器,线程安全和线程不安全的分别有哪些?面试题二: HashMap 的实现原理/底层数据结构? JDK1.7 和 JDK1.8追问一&am…...

java基础—Volatile关键字详解

java基础—Volatile关键字详解 文章目录java基础—Volatile关键字详解并发编程的三大特性:volatile的作用是什么volatile如何保证有可见性volatile保证可见性在JMM层面原理volatile保证可见性在CPU层面原理可见性问题的例子volatile如何保证有序性单例模式使用volat…...

内存检测工具Sanitizers

Sanitizers介绍 Sanitizers 是谷歌开源的内存检测工具,包括AddressSanitizer、MemorySanitizer、ThreadSanitizer、LeakSanitizer。 Sanitizers是LLVM的一部分。 gcc4.8:支持Address和Thread Sanitizer。 gcc4.9:支持Leak Sanitizer和UBSani…...

Triton : OpenAI 开发的用于Gpu开发语言

Triton : OpenAI 开发的用于Gpu开发语言https://openai.com/blog/triton/1、介绍 https://openai.com/blog/triton/ 2、git地址 https://github.com/openai/triton 3、论文 http://www.eecs.harvard.edu/~htk/publication/2019-mapl-tillet-kung-cox.pdf SIMD : Single Inst…...

Python文件操作-代码案例

文章目录文件打开文件open写文件上下文管理器第三方库简单应用案例使用python生成二维码使用python操作excel程序员鼓励师学生管理系统文件 变量就在内存中,文件在硬盘中. 内存空间更小,访问速度快,成本贵,数据容易丢失,硬盘空间大,访问慢,偏移,持久化存储. \\在才是 \的含义…...

活动目录(Active Directory)管理,AD自动化

每个IT管理员几乎每天都在Active Directory管理中面临许多挑战,尤其是在管理Active Directory用户帐户方面。手动配置用户属性非常耗时、令人厌烦且容易出错,尤其是在大型、复杂的 Windows 网络中。Active Directory管理员和IT经理大多必须执行重复和世俗…...

Allegro如何使用Vertext命令修改丝印线段的形状操作指导

Allegro如何使用Vertext命令修改丝印线段的形状操作指导 在用Allegro画丝印线段的时候,如果画了一段不是自己需要形状的线段,无需删除重画,可以用Vertext命令直接编辑 如下图 修改前 修改后 具体操作如下 选择Edit...

Leetcode力扣秋招刷题路-0030

从0开始的秋招刷题路,记录下所刷每道题的题解,帮助自己回顾总结 30. 串联所有单词的子串 给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。…...

基于Prometheus和k8s搭建监控系统

文章目录1、实验环境2、Prometheus介绍?3、Prometheus特点3.1 样本4、Prometheus组件介绍5、Prometheus和zabbix对比分析6、Prometheus的几种部署模式6.1 基本高可用模式6.2 基本高可用远程存储6.3 基本HA 远程存储 联邦集群方案7、Prometheus的四种数据类型7.1 C…...

类和对象(下)

类和对象(下)再谈构造函数构造函数体赋值初始化列表explicit关键字static成员静态成员的特性友元友元函数友元类成员函数做友元内部类匿名对象编译器的一些优化再谈构造函数 构造函数体赋值 在创建对象的时候编译器会调用构造函数给对象中的成员变量一…...

达梦数据库单机部署

一、安装前准备 1. 安装环境 操作系统:redhat7.9 达梦数据库版本:V8 内存:2G CPU:x86_64 2. 新建用户组和用户 groupadd dinstall useradd -g dinstall -m -d /home/dmdba -S /bin/bash dmdba passwd dmdba3. 配置参数 vi /etc/security/limits.conf #在末尾添加以下内…...

从零到一学习Flutter——(二)状态和路由

背景 前文提到了Widget的状态,在Flutter中一切都是Widget,那么由Widget组成的页面,会有很多复杂的父子关系,要想交互友好,则需要这些Widget进行通讯,也就是所谓的状态管理。 同时在了解了布局之后,我们会写出很多的页面,那么在这些页面切换,也是一个很重要的能力。 …...

TC358774XBG/TC358775XBG替代方案|CS5518替代TC358774XBG/TC358775XBG设计DSI转LVSD设计资料

TC358774XBG/TC358775XBG替代方案|CS5518替代TC358774XBG/TC358775XBG设计DSI转LVSD设计资料 TC358774XBG/TC358775XBG 芯片的主要功能是作为 DSI - LVDS 通信协议桥接,主芯片的视频数据可通过 DSI 链路流 出,以驱动兼容 LVDS 的显示板。换句话说&#x…...

Linux---Kernal与Shell讲解

目录 Shell简介 什么是Shell Shell分类 内核Kernal Shell简介 什么是Shell 我们首先需要知道一台完整的计算机是由硬件组成的,而人不可以直接与硬件交互,为了完成交互,进行了以下的操作 将硬件设备交由内核管理,给硬件套个内…...

Thiol-PEG-Acid,HS-PEG-COOH,巯基-聚乙二醇-羧基试剂供应

一:产品描述 1、名称 英文:HS-PEG-COOH,Thiol-PEG-Acid 中文:巯基-聚乙二醇-羧基 2、CAS编号:N/A 3、所属分类:Carboxylic acid PEG Thiol PEG 4、分子量:可定制,Thiol-聚乙二…...

数据结构与算法基础-学习-09-线性表之栈的理解、初始化顺序栈、判断顺序栈空、获取顺序栈长度的实现

一、个人理解栈是线性表的一种衍生,和之前的顺序表和链表在插入和删除元素上有较大差异,其他基本相同,栈是数据只能插入表的尾部(栈顶),删除数据时只能删除表的尾部(栈顶)数据&#…...

深入Kafka核心设计与实践原理读书笔记第二章

1 生产者 生产逻辑 配置生产者客户端参数及创建相应的生产者实例。构建待发送的消息。发送消息关闭实列 参数说明 bootstrap.servers :用来指定生产者客户端链接Kafka集群搜需要的broker地址清单,具体格式 host1:port1,host2:port2,可以设置一个或多…...

知乎kol投放怎么做?知乎kol资源从哪里找?

每个领域都有一些比较专业且具有话语权的大V博主,他们推荐某个产品或是品牌就能对粉丝产生很深的影响力,影响用户消费决策。 互联网时代,每个热门的内容平台上都活跃着一大批kol博主,做kol投放具有很高的商业价值。 知乎内容社区…...

python设计模式-享元设计模式,抽象工厂设计模式,面向对象设计模式

享元设计模式 享元(flyweight)设计模式属于结构设计模式类别。 它提供了一种减少对象数的方法。 它包含各种有助于改进应用程序结构的功能。享元对象最重要的特性是不可变的。 这意味着一旦构建就不能修改它们。 该模式使用HashMap来存储引用对象 如何实现享元(flyweight)设计…...

10条终身受益的Salesforce职业发展建议!

Salesforce这个千亿美金巨兽,在全球范围内有42,000多名员工。作为一家发展迅速的科技公司,一直在招聘各种角色,包括销售、营销、工程师和管理人员等。 据IDC估计,从2016年到2020年,该生态系统创造了190万个工作岗位。…...

电子科技大学人工智能期末复习笔记(四):概率与贝叶斯网络

目录 前言 概率 概率公式 贝叶斯公式 链式条件概率 例题 1. 求联合概率分布/边缘概率分布/条件概率分布 2. 灵活运用贝叶斯公式 概率总结 贝叶斯网络 判断独立性 两个事件独立的判断 条件独立性的判断 假设条件独立的链式法则 ⚠Active / Inactive Paths 判断独…...

码上掘金实现电子木鱼

前言 前几天在朋友圈看到“敲电子木鱼”的视频,敲一下木鱼就提示“功德 1”,还带有敲击声和念经的声音,感觉挺有意思的。 心血来潮,捣鼓了一晚上,借助码上掘金实现了这个功能。 展示效果 素材 准备素材如下&#…...

深度学习_L2正则化

文章目录参考博客正则化介绍正则化的实现参考博客 深入理解L1、L2正则化 PyTorch 实现L2正则化以及Dropout的操作 正则化介绍 正则化(Regularization)是机器学习中一种常用的技术,其主要目的是控制模型复杂度,减小过拟合。最基…...

聊城手机网站建设费用/不需要验证码的广告平台

摘要vue和axios都可以使用es6-promise来实现f1().then(f2).then(f3)这样的连写形式,es6-promise其实现代浏览器已经支持,无需加载外部文件。由于promise写法明显由于传统写法,已经越来越被高级程序采用,不懂promise就没法看高级程…...

宁波梅山建设局网站/搜索引擎优化的常用方法

CWinThread::m_pMainWnd该成员变量去存储你的线程主窗口对象。当和m_pMainWnd 相关的窗口被关闭后,MFC会自动终止你的线程。如果该线程是应用程序主线程,程序也将会被终止。如果该数据成员为NULL,应用程序CWinApp对象的主窗口将用来决定什么时…...

精品课网站怎么做/好的seo网站

值传递!Java中只有按值传递,没有按引用传递! 回家后我就迫不及待地查询了这个问题,觉得自己对java这么基础的问题都搞错实在太丢人! 综合网上的描述,我大概了解了是怎么回事,现在整理如下&#…...

洛阳疾控最新通告今天/seo官网优化怎么做

1、标识符由字母、数字、下划线组成;2、标识符不能以数字开头;3、标识符区分大小写;PS:以下划线开头的标识符具有特殊意义: 以单下划线开头 (如_init) 为不能直接访问的类属性,必须通过类提供的…...

c2c网站开发策划/公众号软文是什么意思

Latex基本语法的备忘录 Latex基本语法前言一、常见的数学符号1. 数学上的“**属于**”、“**不属于**”符号。2. 数学的矩阵的**转置符号**书写。3. 数学中求和公式。4.数学中字母代上标5.数字或公式上添加方框6.数学分式7.数学公式中在某些符号下添加花括号8.数学中的垂直符号…...

大理中小企业网站建设/营销推广软文案例

CK_Label_v13一、产品参数 1. 电池供电版 产品型号 CK_Label_v13 尺寸 70x34.7x13.6mm 屏幕尺寸 2.1 inch 分辨率 250*122 像素密度 130dpi 显示技术 电子墨水屏显示 显示颜色 黑/白 外观颜色 黑色 按键 1 指示灯 1 RGB灯 灯光颜色 7种(红/绿/蓝/黄/紫/白…...