Go——defer陷阱

2023-05-16

defer陷阱

defer带来了两个副作用:

  1. 对返回值的影响
  2. 对性能的影响

defer和函数返回值

defr中如果引用了函数的返回值,则因引用形式不同会导致不同的结果,这些结果往往给初学者造成很大的困惑,我们先来看一下如下三个函数的执行结果:

package main

func main() {
	println("f1=", f1())//f1= 1
	println("f2=", f2())//f2= 5
	println("f3=", f3())//f3= 1
}

func f1() (r int) {
	defer func() {
		r++
	}()
	return 0
}

func f2() (r int) {
	t := 5
	defer func() {
		t = t + 5
	}()
	return t
}

func f3() (r int) {
	defer func(r int) {
		r = r + 5
	}(r)
	return 1
}

绝大多数初学者看到执行结果都会很茫然,到底是什么原因导致的这个结果呢?我们接下来逐个分析。1、2、3三个函数的共同点就是它们都是带命名返回值的函数,返回值都是变量r。

在函数章节已经了解到:

  1. 函数调用方负责开辟栈空间,包括形参和返回值的空间。
  2. 有名的函数返回值相当于函数的局部变量,被初始化为类型的零值。

现在分析一下f1,defer语句后面的匿名函数是对函数返回值r的闭包引用,f1函数的逻辑如下:

  1. r是函数的有名返回值,分配在栈上,其地址又被称为返回值所在栈区。首先r被初始化为0。
  2. “return 0”会复制0到返回值栈区,返回值r被赋值为0。
  3. 执行defer语句,由于匿名函数对返回值r是闭包引用,所以r++执行后,函数返回值被修改为1。
  4. defer语句执行完后RET返回,此时函数的返回值仍然为1。

f1的程序指令虚列如下:
在这里插入图片描述
在这里插入图片描述

同理来分析函数2的逻辑:

  1. 返回值r被初始化为0。
  2. 引入局部变量t,并初始化为5。
  3. 复制t的值5到返回值r所在的栈区。
  4. defr语句后面的匿名函数是对局部变量t的闭包引用,t的值被设置为10。
  5. 函数返回,此时函数返回值栈区上的值仍然是5。

f2的程序指令序列如下:
在这里插入图片描述

最后分析函数3的逻辑:

  1. 返回值r被初始化为0。
  2. 复制1到函数返回值r所在的栈区。
  3. 执行defer,defer后匿名函数使用的是传参数调用,在注册defer函数时将函数返回值r作为实参传进去,由于函数调用的是值拷贝,所以defer函数执行后只是形参值变为5,对实参没有任何影响。
  4. 函数返回,此时函数返回值栈区上的值是1。

f3的程序指令示例如下:
在这里插入图片描述

综上所述,对于带defer的函数返回整体上有三个步骤。

  1. 执行return的值拷贝,将return语句返回的值复制到函数返回值栈区(如果只有一个return,不带任何变量或值,则此步骤不做任何动作)。
  2. 执行defer语句,多个defer按照FILO顺序执行。
  3. 执行调整RET指令。

如果对函数调用是值拷贝、函数闭包及dfr的特性有了解,以及对上面的三条规则熟悉,那么对于这类函数就不应该再有疑惑了。当然在defer中修改函数返回值不是一种明智的编程方法,在实际编程中应尽可能避免此种情况。还有一种彻底解决该问题的方法是,在定义函数时使用不带返回值名的格式。通过这种方式,defer就不能直接引用返回值的栈区,也就避免了返回值被修改的问题,看一下下面的代码。

package main

func main() {
	println("f4=", f4())//0
	println("f5=", f5())//0
}

func f4() int {
	r := 0
	defer func() {
		r++
	}()
	return r
}

func f5() int {
	r := 0
	defer func(i int) {
		i++
	}(r)
	return 0
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Go——defer陷阱 的相关文章

  • 浅谈SpringBoot核心注解原理

    SpringBoot核心注解原理 今天跟大家来探讨下SpringBoot的核心注解 64 SpringBootApplication以及run方法 xff0c 理解下springBoot为什么不需要XML xff0c 达到零配置 首先我们先
  • Quartus II和Modelsim的联合仿真(详细)

    这篇文章不需要在modelsim中建库 映射 建工程等一些繁琐的步骤 xff0c 直接使用modelsim中的默认work库 使用quartus 43 modelsim联合仿真 首先推荐一篇文章 http www cnblogs com e
  • requests.post处理Content-Type: multipart/form-data的请求

    前几天遇到一个需求 xff0c 要调用一个接口发送请求 xff0c 抓包之后得到的数据是这样的 上网看了一些资料得知 xff0c 原来这个接口的数据是通过multipart form data格式传过去的 xff0c multipart f
  • 上一步,下一步(撤销和恢复)

    var data 61 data count 61 0 data list 61 function regain function handleSaveCss 获取workspace body里面的内容 var c 61 34 worksp
  • Ubuntu下dpkg安装软件遇到包依赖问题的处理方法

    造冰箱的大熊猫 64 cnblogs 2019 9 10 向灵魂工程师致敬 xff01 在Ubuntu环境下通过dpkg命令安装deb包时 xff0c 如果遇到包依赖问题 xff0c 如 sudo dpkg i xxx deb Readin
  • Ubuntu18优化桌面版的运行速度

    一 刚开始使用Ubuntu18后 xff0c 感觉开机和运行速度都不理想 xff0c 通过改变一些配置可以提高下用户体验感 二 改变一些配置 a 使用Preload预加载 sudo apt install preload y b 禁用不必要
  • Debian安装mplayer,解决没有声音及声卡独占问题

    通过软件中心可以安装Gnome mplayer 本来以为这样这个播放器已经是 万能 的了 xff0c 但是最近下载了几个 mkv的电影 却发现Gnome mplayer没有办法打开 感觉很失望 在网上找了一番后 说只要下载源代码自己安装就行
  • CentOS7中安装MySQL5.7

    安装必要的组件 yum install y autoconf automake imake libxml2 devel expat devel cmake gcc gcc c 43 43 libaio libaio devel bzr bi
  • 20190708新的开始

    题目描述 发展采矿业当然首先得有矿井 xff0c 小 FF 花了上次探险获得的千分之一的财富请人在岛上挖了 n 口矿井 xff0c 但他似乎忘记考虑的矿井供电问题 为了保证电力的供应 xff0c 小 FF 想到了两种办法 xff1a 在这一
  • Debian安装JDK

    sudo tar zxvf jdk 8u60 linux x64 tar gz C usr local vi bashrc export JAVA HOME 61 usr local jdk1 8 0 60 export JRE HOME
  • Go——多值赋值和短变量声明

    1 多值赋值 可以一次性声明多个变量 xff0c 并可以在声明时赋值 xff0c 而且可以省略类型 xff0c 但必须遵守一定的规则要求 xff0c 具体看下面的示例 如下都是合法的 span class token comment 相同类
  • 「一本通 1.2 练习 2」扩散(loj10015)

    题目描述 一个点每过一个单位时间就会向 4 个方向扩散一个距离 xff0c 如图所示 xff1a 两个点 a b 连通 xff0c 记作 e a b xff0c 当且仅当 a b 的扩散区域有公共部分 连通块的定义是块内的任意两个点 u v

随机推荐

  • .db文件打开方式

    有时在工作中 xff0c 数据库格式db后缀的格式 xff0c 直接是打不开的 xff0c 所以我这里使用了数据库管理工具 xff0c 步骤如下 1 在电脑安装 Navicat Premium xff0c 安装后在桌面生成图标 xff0c
  • MathType的配置问题;将word中的公式转换为mathtype格式失败,缺少OMML2MML.XSL

    安装MathType后打开word报错 打开会出现以下问题 xff1a 首先 xff0c 把startup添加到word的信任中心 xff1a 要确保路径被office信任 依次打开word gt 文件 gt 选项 gt 信任中心 gt 信
  • XMPP系列(四)---发送和接收文字消息,获取历史消息功能

    今天开始做到最主要的功能发送和接收消息 获取本地历史数据 先上到目前为止的效果图 xff1a 首先是要在XMPPFramework h中引入数据存储模块 xff1a 聊天记录模块的导入 import 34 XMPPMessageArchiv
  • linux新增磁盘后,用fdisk等命令查询不到

    ls sys class scsi host xff08 会看到有host0 host1 hostN xff0c 对每个host进行如下操作 xff09 echo 34 34 gt sys class scsi host host0 sca
  • ubuntu上源码编译安装mysql5.7.27

    一 查看操作系统环境和目录结构 xff0c 并创建mysql用户和组 xff0c 以及规划安装mysql所需要的目录 cat etc issue 查看发行版本信息 xff1a cat proc version 查看正在运行的内核版本信息 u
  • (转-收集)MSSQL手工注入语句集合

    and exists select from sysobjects 判断是否是MSSQL and exists select from tableName 判断某表是否存在 tableName为表名 and 1 61 select 64 6
  • 滚动视图 UIScrollView

    UIScrollView xff1a 提供可以显 示 大于应 用窗 口的内容功能的控件 用户可以通过 手势使内容滚动和缩放 从 而查 看全部内容 初始化一个UIScrollView的对象 1 UIScrollView scroll 61 U
  • 基于steam的游戏销量预测 — PART 1 — 爬取steam游戏相关数据的爬虫

    语言 xff1a python 环境 xff1a ubuntu 爬取内容 xff1a steam游戏标签 xff0c 评论 xff0c 以及在 steamspy 爬取对应游戏的销量 使用相关 xff1a urllib xff0c lxml
  • WechatHelper

    using System using System Collections Generic using System Configuration using System IO using System Linq using System
  • Go——range复用临时变量

    range复用临时变量 span class token keyword package span main span class token keyword import span span class token string 34 s
  • cf 1169 C Increasing by Modulo

    cf 1169 C Increasing by Modulo 题意 给你一个n个数字的序列 xff0c 有一个操作是选其中的一些数字来 43 1 xff0c 最后使得序列每一个数取模m后是一个非严格单调递增的序列 xff0c 问至少需要多少
  • 洛谷P1605 迷宫 题解

    洛谷P1605 迷宫 题解 题目背景 问题描述 数据规模 输入 输出 输入输出样例 输入样例 1 xff1a 输出样例 1 xff1a 题解 C 43 43 代码 题目背景 问题描述 给定一个N M方格的迷宫 xff0c 迷宫里有T处障碍
  • linux禁用nouveau grub,NVIDIA驱动安装之禁用nouveau

    最近实验室的服务器英伟达驱动重启之后就不能用了 xff0c 查明原因原来是因为Ubuntu自动升级了内核 xff0c 导致原本的驱动失效了 xff0c 所以一定不要没事干去升级内核 xff0c 下面介绍一下重装驱动的曲折之旅 0x00更改b
  • Python:使用Kivy将python程序打包为apk文件

    1 概述 Kivy是一套Python下的跨平台开源应用开发框架 xff0c 官网 xff0c 我们可以用 它来将Python程序打包为安卓的apk安装文件 以下是在windows环境中使用 安装和配置的过程中会下载很多东西 xff0c 确保
  • Ubuntu16.04进入挂起或休眠状态时按任何键都无法唤醒问题解决办法

    挂起 xff08 待机 xff09 计算机将目前的运行状态等数据存放在内存 xff0c 关闭硬盘 外设等设备 xff0c 进入等待状态 此时内存仍然需要电力维持其数据 xff0c 但整机耗电很少 恢复时计算机从内存读 出数据 xff0c 回
  • java 兔子繁殖问题_兔子繁殖问题

    兔子繁殖问题 设有一对新生的兔子 xff0c 从第三个月开始他们每个月都生一对兔子 xff0c 新生的兔子从第三个月开始又每个月生一对兔子 按此规律 xff0c 并假定兔子没有死亡 xff0c 20个月后共有多少个兔子 xff1f 要求编写
  • 尝试一下sql server2016里面的json功能

    前2天下载了一个2016的rc版本来玩一下 xff0c 首先感觉是 开发者版本免费啦 xff01 xff01 撒花 xff01 xff01 xff01 另外一个东西 sql server 2016能支持json 的解析和应用啦 xff0c
  • MPICH 3.2安装

    step 1 下载 在官网下载最新版 http www mpich org downloads step 2 解压安装 tar zxvf mpich 3 2 tar gz step 3 进入mpich 3 2 文件夹 xff0c 并配置安装
  • WEB网站无法打开某种格式资源的解决办法

    近日 xff0c 在发布网站的时候发现了一个问题 大致情况描述为 xff1a 网站中所有的文件类型的附件存放在同一个文件夹下 xff0c 可是在页面上 xff0c doc格式 tiff格式的文件都可以正常打开 xff0c 唯独ceb格式的文
  • Go——defer陷阱

    defer陷阱 defer带来了两个副作用 xff1a 对返回值的影响对性能的影响 defer和函数返回值 defr中如果引用了函数的返回值 xff0c 则因引用形式不同会导致不同的结果 xff0c 这些结果往往给初学者造成很大的困惑 xf