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 aa当前值(0) 作为返回值
  2. 执行 defera 变成 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. 执行 defera++返回值变量 改成 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 面试必考考点之一!

相关推荐
蓝宝石的傻话20 小时前
rpi-cam:给 Raspberry Pi 造的轻量级 ONVIF 相机服务
go·iot·nvr
蓝宝石的傻话1 天前
VictoriaMetrics指标流聚合三年回顾与现状(2026)
go·prometheus·victoriametrics
踏着七彩祥云的小丑1 天前
Go学习第7天:Map集合 + 递归函数 + 类型转换
开发语言·学习·golang·go
踏着七彩祥云的小丑2 天前
Go 学习第6天:结构体 + 切片 + range遍历
开发语言·学习·golang·go
壮Sir不壮2 天前
GO语言——GMP调度模型
linux·开发语言·golang·go·操作系统·线程·协程
ShuiShenHuoLe2 天前
go语言time模块
go
2601_951645783 天前
Linux 编程语言全解析:C、C++、Python、Go、Rust 谁更强?
linux·python·go·c·编程语言
踏着七彩祥云的小丑3 天前
Go学习第5天:变量作用域 + 数组 + 指针
开发语言·学习·golang·go
唐青枫3 天前
别再把 struct 只当字段集合:Go 结构体从语法到项目实战
go