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(已经固定)
}
执行顺序:
return a把a的当前值(0) 作为返回值- 执行
defer,a变成1 - 但返回值已经是
0,不会改变
结果:0
函数 f3():命名返回值
go
func f3() (a int) { // 返回值命名,a就是返回值变量
defer func() {
a++ // ③ 直接修改返回值变量
}()
return a // ① 返回语句:把 a(0) 作为返回值
// ② 执行 defer,a变成1
// ④ 返回 1(返回值变量被改了)
}
执行顺序:
return a设置返回值 =a(此时a=0)- 执行
defer,a++把返回值变量 改成1 - 函数返回
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 面试必考考点之一!