Go 语言 defer 在命名返回值 和 匿名返回值 函数中的表现不一样

go 复制代码
func f2() int {
	var a int
	defer func() {
		a++
	}()

	return a
}

func f3() (a int) {
	defer func() {
		a++
	}()

	return a
}

fmt.Println(f2()) //0

fmt.Println(f3()) //1

这是一个经典的 defer + 命名返回值 的陷阱,也是面试高频题。


🎯 核心结论

defer 可以修改命名返回值,但不能修改匿名返回值。

  • 匿名返回值 :return 时值已经固定,defer 修改的是局部变量,不影响返回值
  • 命名返回值 :return 时只是将返回值变量赋给结果,defer 可以继续修改这个变量

🔍 详细拆解

函数 f2():匿名返回值

go 复制代码
func f2() int {          // 返回值匿名
    var a int            // a = 0
    defer func() {
        a++              // ③ 修改局部变量 a(a变成1)
    }()
    return a             // ① 返回值 = a(0)
                         // ② 执行 defer
                         // ④ 返回 0(已经固定)
}

执行顺序:

  1. return a 把 a 的当前值(0) 作为返回值
  2. 执行 defer,a 变成 1
  3. 但返回值已经是 0,不会改变

结果:0


函数 f3():命名返回值

go 复制代码
func f3() (a int) {      // 返回值命名,a就是返回值变量
    defer func() {
        a++              // ③ 直接修改返回值变量
    }()
    return a             // ① 返回语句:把 a(0) 作为返回值
                         // ② 执行 defer,a变成1
                         // ④ 返回 1(返回值变量被改了)
}

执行顺序:

  1. return a 设置返回值 = a(此时 a=0)
  2. 执行 defer,a++ 把返回值变量 改成 1
  3. 函数返回 1

结果:1


📊 执行顺序图

复制代码
f2():                      f3():
┌─────────────────┐       ┌─────────────────┐
│ a = 0           │       │ a = 0           │
│                 │       │                 │
│ return a ───────┼───┐   │ return a ───────┼───┐
│   ↓             │   │   │   ↓             │   │
│ 返回值 = 0      │   │   │ 返回值 = 0      │   │
│                 │   │   │                 │   │
│ defer a++       │   │   │ defer a++       │   │
│   ↓             │   │   │   ↓             │   │
│ a = 1           │   │   │ a = 1           │   │
│                 │   │   │ 返回值 = 1 ←────┘   │
│ 返回 0          │   │   │                 │   │
└─────────────────┘   │   │ 返回 1          │   │
                      │   └─────────────────┘   │
                      └─────────────────────────┘

💡 记忆技巧

返回值类型 defer 能修改返回值吗 原因
匿名 func f() int ❌ 不能 返回值是副本
命名 func f() (x int) ✅ 能 返回值是同一个变量

一句话:

命名返回值 = 同一个变量,defer 可以改;匿名返回值 = 快照,改了也没用。


🧪 验证小实验

go 复制代码
func f2_5() int {
    var a int = 10
    defer func() { a = 999 }()
    return a
}
// 结果:10(不是999)

func f3_5() (a int) {
    a = 10
    defer func() { a = 999 }()
    return a
}
// 结果:999

📌 总结

go 复制代码
f2() → 0   // 匿名返回值,defer 改的是局部变量
f3() → 1   // 命名返回值,defer 改的是返回值本身

面试高频变形题:

go 复制代码
func f4() (r int) {
    defer func() { r += 100 }()
    return 5
}
// 结果:105(先 r=5,defer 加100,返回105)

这是 Go 面试必考考点之一!

相关推荐
大然Ryan1 天前
我用 Go 和 AI 做了一个 macOS 磁盘清理工具:Vole(开源)
go
我不会起名字3221 天前
seq 空洞排查:Mongo 的 51 只是预留上界,Redis CURR 才是已消耗边界
redis·后端·mongodb·go·即时通讯·im·故障排查
喵个咪2 天前
GoWind 拆解:全链 ID 类型体系——int64 定版与前端字符串化契约
前端·后端·go
喵个咪2 天前
GoWind CMS 拆解:发布审批——多级推进、逐级角色与全程留痕
前端·后端·go
喵个咪2 天前
GoWind Flow 拆解:一个 Go 工作流引擎与它的双模式设计器
前端·后端·go
喵个咪2 天前
GoWind CMS 拆解:搜索三板斧——高亮、联想、同义词
后端·搜索引擎·go
喵个咪2 天前
GoWind 拆解:AI Agent 编排引擎——波次并行、子图递归与人工审批
人工智能·后端·go
喵个咪2 天前
GoWind Admin 拆解:打印模板设计器——从画布到服务端 PDF 的完整打印链路
前端·后端·go
喵个咪2 天前
GoWind Admin 拆解:BI 大屏设计器——schema 即 JSON、resolver 注入与三张快照表的口径谱系
大数据·后端·go
Warson_L3 天前
高性能架构设计
python·go