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

相关推荐
No8g攻城狮3 小时前
【AI工具】wsl2 + ubuntu22.04安装部署sub2api详细教程
人工智能·ai·go·vue
明月_清风1 天前
Go 没有 `class`,如何实现面向对象三要素?与传统 OOP 的深度对比
后端·go
审判长烧鸡1 天前
【GO context 】上下文取消/超时的本质
go·context·上下文·ai问答
java知路1 天前
解决 Go 编译速度慢的问题
go
审判长烧鸡2 天前
【Go Interface】接口诞生的意义
go·接口·interface
审判长烧鸡2 天前
【Go i18n】TOML语言包
go·i18n·语言包
用户398346161202 天前
Go-Spring 实战第 10 课 —— 依赖注入的方式:字段注入和构造函数注入
spring·go
用户398346161202 天前
Go-Spring 实战第 9 课 —— IoC 容器:复杂 Go 应用如何统一对象装配
spring·go
审判长烧鸡2 天前
【Go Generics】泛型为何而生的
go·泛型·overload·重载·generics