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 面试必考考点之一!

相关推荐
2651940511264802 小时前
08-告警并发与多Master同步
go·监控
程序员爱钓鱼5 小时前
Go for 循环详解
后端·面试·go
程序员爱钓鱼21 小时前
Go switch 详解
后端·面试·go
2651940511264801 天前
07-告警引擎与状态机
go·监控
NutShell Wang1 天前
Wails v3 Beta 实战:用显式对象模型重写你的第一个 Go 桌面应用
前端·人工智能·go·vibe coding
SoStraw1 天前
Go + webrpc 实战:人在外面,远程查家里 NAS 磁盘和目录
go·p2p·nas·cgo·json-rpc·webrpc·无公网ip
newerp2 天前
Go net/http 标准库基础
后端·程序员·go
2651940511264802 天前
01-整体架构与高可用
go
程序员爱钓鱼2 天前
Go if 判断详解
前端·后端·go
名字还没想好☜3 天前
Go 的 unsafe.Pointer 实战:零拷贝 []byte↔string 转换与三条铁律
开发语言·后端·golang·go·unsafe