先来分享一个很多人踩过的坑:很多人以为defer是“函数返回后才执行”,但实际上它的执行时机更微妙——是在函数控制流离开当前栈帧之前。这个时间点,比你想象得要更靠后,搞清楚它对写出靠谱的Go代码至关重要。

defer 的参数在注册时就完成求值

这个是初学者最容易掉进去的陷阱之一。举个例子:

func example() {    x := 10    defer fmt.Println("x =", x) // 此处 x 被拷贝为 10    x = 20    return}

输出一定是 x = 10,不是 20。原因很简单:defer 后面表达式中所有参数,在执行到该 defer 语句那一行时就已经完成计算并保存了。这个规则的影响面还挺广的:

多个 defer 按 LIFO 顺序执行

不是按代码顺序,而是“最后声明的最先执行”。这个顺序决定了资源释放是否合理:

func openAndClose() {    f1, _ := os.Open("a.txt")    defer f1.Close()    f2, _ := os.Open("b.txt")    defer f2.Close()    // 实际执行顺序:f2.Close() → f1.Close()}

defer 在 return 之后、函数真正退出之前运行

这个“之间”阶段很微妙:返回值已写入栈帧,但函数还没退出。所以命名返回值可被 defer 修改:

func counter() (ret int) {    ret = 1    defer func() { ret += 10 }() // 这里能改 ret    return // 返回值 ret=1 已确定,但 defer 还没跑;defer 跑完才真正返回}

输出是 11。需要留意的是:

defer 性能开销在 Go 1.14+ 已基本可忽略

早期版本 defer 有明显栈分配和链表操作成本,但现在情况已大为改观:

真正要警惕的不是性能,而是逻辑错位:比如在 defer 里调用可能 panic 的函数,又没 recover,会导致 panic 被延迟传播,堆栈信息指向错误位置。

本文转载于:https://www.php.cn/faq/2341059.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。