导航菜单

defer 执行顺序

🟢 简单

题目描述

以下代码的输出是什么?

func main() {
    defer fmt.Println(1)
    defer fmt.Println(2)
    defer fmt.Println(3)
    fmt.Println(0)
}

期望输出

0
3
2
1

进阶问题

以下代码的输出是什么?

func main() {
    i := 0
    defer fmt.Println(i)
    i = 10
    fmt.Println("hello")
}

期望输出

hello
0

提示

  • defer 的执行顺序是 LIFO(后进先出)
  • defer 的参数在声明时立即求值

解法一:理解 defer 执行顺序

参考答案 (2 个标签)
defer LIFO

核心概念

defer 语句的执行特点:

  1. 执行时机:函数返回前执行
  2. 执行顺序:LIFO(Last In First Out,后进先出)
  3. 参数求值:声明时立即求值(而非执行时)

图解

函数调用栈(defer 声明顺序):

    ┌─────────────┐
    │ defer 1     │  最后声明
    ├─────────────┤
    │ defer 2     │
    ├─────────────┤
    │ defer 3     │  最先声明
    └─────────────┘

执行顺序(defer 执行顺序):3 → 2 → 1

代码示例

func example() {
    defer fmt.Println(1) // 栈底
    defer fmt.Println(2)
    defer fmt.Println(3) // 栈顶
    fmt.Println(0)
}

// 输出:
// 0        <- 普通语句先执行
// 3        <- defer 后执行,栈顶先出
// 2
// 1

解法二:理解 defer 参数求值时机

参考答案 (2 个标签)
defer 参数求值

核心概念

defer 的参数在声明时立即求值,而不是执行时。

func example() {
    i := 0
    defer fmt.Println(i) // i = 0,立即求值
    i = 10              // 修改 i 不影响 defer 的参数
}

// 输出:0

如果想获取最新值

func example() {
    i := 0
    defer func() {
        fmt.Println(i) // 闭包,获取最新值
    }()
    i = 10
}

// 输出:10

defer 修改返回值

func example() (result int) {
    defer func() {
        result++ // 修改命名返回值
    }()
    return 0
}

// 返回值:1

对比:值类型 vs 引用类型

func valueExample() {
    i := 0
    defer fmt.Println(i) // 输出 0(值类型)
    i = 10
}

func referenceExample() {
    m := map[string]int{"key": 0}
    defer fmt.Println(m["key"]) // 输出 0(立即求值)
    
    // defer 闭包获取最新值
    defer func() {
        fmt.Println(m["key"]) // 输出 10
    }()
    
    m["key"] = 10
}

常见陷阱

陷阱 1:循环中的 defer

func main() {
    for i := 0; i < 3; i++ {
        defer fmt.Println(i)
    }
}

// 输出:2, 1, 0
// 原因:LIFO,且 i 是值类型,每次循环 i 的值被捕获

陷阱 2:defer 和 panic

func main() {
    defer fmt.Println("defer 1")
    panic("panic")
    defer fmt.Println("defer 2") // 不会执行
}

// 输出:
// defer 1
// panic: panic

陷阱 3:defer recover

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("recovered:", r)
        }
    }()
    
    panic("error")
}

// 输出:recovered: error

搜索