Go 中 defer 的机制

文章目录

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

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

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


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

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

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

输出:

复制代码
main 逻辑
defer 2
defer 1

结论

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

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

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

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

输出:

复制代码
main 中的 x: 20
defer 中的 x: 10

结论

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

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

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

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

输出:

复制代码
main 中的 x: 20
defer 中的 x: 20

结论

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

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

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

示例 4:值返回与指针返回的差异
go 复制代码
// 值返回:defer 修改不影响返回值
func f1() int {
    x := 10
    defer func() { x++ }()
    return x // 实际返回的是 x 的拷贝
}

// 指针返回:defer 修改影响返回值
func f2() *int {
    x := 10
    defer func() { x++ }()
    return &x // 返回 x 的地址
}

func main() {
    fmt.Println(f1()) // 输出 10
    fmt.Println(*f2()) // 输出 11
}

结论

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

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

相关推荐
波波00716 小时前
ASP.NET Core 健康检查实战:不只是一个 /health 接口
后端·asp.net
小码哥_常16 小时前
Spring Boot 搭建邮件发送系统:开启你的邮件自动化之旅
后端
故事和你9117 小时前
洛谷-数据结构1-1-线性表1
开发语言·数据结构·c++·算法·leetcode·动态规划·图论
石榴树下的七彩鱼17 小时前
图片修复 API 接入实战:网站如何自动去除图片水印(Python / PHP / C# 示例)
图像处理·后端·python·c#·php·api·图片去水印
我叫黑大帅18 小时前
为什么TCP是三次握手?
后端·网络协议·面试
我叫黑大帅18 小时前
如何排查 MySQL 慢查询
后端·sql·面试
techdashen18 小时前
Rust项目公开征测:Cargo 构建目录新布局方案
开发语言·后端·rust
星空椰18 小时前
JavaScript 进阶基础:函数、作用域与常用技巧总结
开发语言·前端·javascript
消失的旧时光-194318 小时前
Spring Boot 实战(五):接口工程化升级(统一返回 + 异常处理 + 错误码体系 + 异常流转机制)
java·spring boot·后端·解耦
忒可君18 小时前
C# winform 自制分页功能
android·开发语言·c#