Go 中 defer 的机制

news/2025/2/7 13:17:22/

文章目录

    • Go 语言中 `defer` 的底层机制与实战解析
      • 一、`defer` 的执行顺序:后进先出(LIFO)
        • 示例 :多个 `defer` 的执行顺序
      • 二、`defer` 的参数预计算:值拷贝的陷阱
        • 示例 :参数预计算的影响
      • 三、`defer` 与闭包:动态绑定的变量
        • 示例 :闭包中的变量绑定
      • 四、`defer` 与返回值:隐式的赋值逻辑
        • 示例 4:值返回与指针返回的差异

Go 语言中 defer 的底层机制与实战解析

defer 是 Go 语言中用于延迟执行函数调用的关键字,常用于资源清理(如关闭文件、释放锁)和异常处理。但其行为机制存在一些隐蔽的细节,稍有不慎可能导致难以察觉的 Bug。本文通过多个直观示例,深入剖析 defer 的核心机制。


一、defer 的执行顺序:后进先出(LIFO)

多个 defer 语句按逆序执行,类似于栈的“后进先出”原则。

示例 :多个 defer 的执行顺序
func main() {defer fmt.Println("defer 1")defer fmt.Println("defer 2")fmt.Println("main 逻辑")
}

输出:

main 逻辑
defer 2
defer 1

结论

  • defer 语句按注册顺序的逆序执行,确保依赖资源按正确顺序释放(如先打开的文件后关闭)。

二、defer 的参数预计算:值拷贝的陷阱

defer 的参数在注册时即被预计算并拷贝,而非执行时动态获取。

示例 :参数预计算的影响
func main() {x := 10defer fmt.Println("defer 中的 x:", x) // x 的值在注册时被拷贝x = 20fmt.Println("main 中的 x:", x)
}

输出:

main 中的 x: 20
defer 中的 x: 10

结论

  • 若参数是值类型(如 intstring),defer 会拷贝当前值,后续修改不影响已注册的 defer
  • 若参数是指针或引用类型(如 *intslice),拷贝的是地址,后续修改会影响 defer 的执行结果。

三、defer 与闭包:动态绑定的变量

defer 函数若使用外部变量(闭包),会引用变量的最新值,而非注册时的值。

示例 :闭包中的变量绑定
func main() {x := 10defer func() {fmt.Println("defer 中的 x:", x) // 闭包引用最新值}()x = 20fmt.Println("main 中的 x:", x)
}

输出:

main 中的 x: 20
defer 中的 x: 20

结论

  • 闭包中的变量在 defer 执行时才求值,因此会反映变量的最终状态。
  • 若需固定闭包中的值,需在注册时通过参数传递(如 defer func(a int) { ... }(x))。

四、defer 与返回值:隐式的赋值逻辑

defer 中修改返回值的行为取决于返回值的定义方式(值返回 vs 指针返回)。

示例 4:值返回与指针返回的差异
// 值返回:defer 修改不影响返回值
func f1() int {x := 10defer func() { x++ }()return x // 实际返回的是 x 的拷贝
}// 指针返回:defer 修改影响返回值
func f2() *int {x := 10defer func() { x++ }()return &x // 返回 x 的地址
}func main() {fmt.Println(f1()) // 输出 10fmt.Println(*f2()) // 输出 11
}

结论

  • 值返回:返回值在 return 时被拷贝,defer 修改原变量不影响已拷贝的值。
  • 指针返回:返回的是变量地址,defer 通过地址修改原变量,影响最终结果。

若有错误与不足请指出,关注DPT一起进步吧!!!


http://www.ppmy.cn/news/1570074.html

相关文章

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_vslprintf 函数

ngx_vslprintf 声明 ngx_vslprintf的声明在 ngx_string.h 中: u_char *ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args); ngx_vslprintf 实现 ngx_string.c 中 ngx_vslprintf 函数的定义 u_char * ngx_vslprintf(u_char *buf, u_char…

vscode修改自定义模板

vscode修改自定义模板 打开命令面板:你可以通过快捷键 CtrlShiftP(Windows/Linux)或CmdShiftP(macOS)打开命令面板。输入并选择“首选项: 配置用户代码片段”:在命令面板中输入 Preferences:Configure Use…

PEP8代码规范

文章目录 波浪线 波浪线 不同UI下可能会有差异,但可做通用参考 红色 代码错误,必须处理!代码才能正常运行 – 代码未顶格 – 代码未写完灰色 建议改进,不会影响代码正常执行,可以通过ide的格式规范调整 – 注释缺少空…

可靠冲突性多视角学习:通过证据驱动的多视角融合提供决策可靠性

2024年2月28日,由多位作者联合提出了一种名为可靠冲突性多视角学习(RCML)的框架,其核心贡献在于为含有冲突性实例的多视角数据提供决策结果与可靠性评估。该方法通过证据驱动的多视角融合(ECML)策略&#x…

【分布式架构理论3】分布式调用(2):API 网关分析

文章目录 一、API 网关的作用1. 业务层面:简化调用复杂性2. 系统层面:屏蔽客户端调用差异3. 其他方面: 二、API 网关的技术原理1. 协议转换2. 链式处理3. 异步请求机制1. Zuul1:同步阻塞处理2. Zuul2:异步非阻塞处理 三…

HTTP异步Client源码解析

我们知道Netty作为高性能通信框架,优点在于内部封装了管道的连接通信等操作,用户只需要调用封装好的接口,便可以很便捷的进行高并发通信。类似,在Http请求时,我们通过调用HttpClient,内部使用java NIO技术&…

使用 cipher /w 清除磁盘删除残留数据(Windows) - 随笔

cipher命令是Windows 系统自带的一个用于管理文件加密和磁盘数据清除的工具。通过 cipher /w 命令,可以清除磁盘上已删除文件的残留数据,确保这些数据无法被恢复。以下是一个简易的批处理脚本,用于清除指定磁盘上的加密数据。 echo off :: 清…

排序算法与查找算法

1.十大经典排序算法 我们希望数据以一种有序的形式组织起来&#xff0c;无序的数据我们要尽量将其变得有序 一般说来有10种比较经典的排序算法 简单记忆为Miss D----D小姐 时间复杂度 &#xff1a;红色<绿色<蓝色 空间复杂度&#xff1a;圆越大越占空间 稳定性&…