说好 defer 在 return 之后执行,为什么结果却不是?

阅读本文大概需要 3 分钟。

大家好,我是 polarisxu。

Go 语言爱好者周刊第 97 期有一道题目:以下代码输出什么?

package main

import (
 "fmt"
)

func hello(i *int) int {
 defer func() {
  *i = 19
 }()
 return *i
}

func main() {
 i := 10
 j := hello(&i)
 fmt.Println(i, j)
}

答题结果如下:

正确答案:19 10,正确率堪忧,才 32%。

这篇文章简单解释下。

我们先看 i 的值。因为 hello 接收的是指针,因此里面对参数值的修改会改变传递的值,这个不需要关心是不是在 defer 里面。所以,i 最终的值是 19。从投票结果看,i = 19 答对人数较多。

再来看 j 的值,即 hello 函数的返回值,在这个函数中,return 语句处,*i 的值是 10,迷惑点在于 defer 中的 *i = 19。大家都知道 defer 语句在 return 语句之后执行,相信很多人也看到过类似的题目,defer 修改了函数返回的值。但那是针对命名返回值。比如 hello 改为这样:

func hello(i *int) (j int) {
  defer func() {
    j = 19
  }()
  j = *i
  return j
}

这时候,虽然 return 时,j 的值是 10,但在 defer 语句中 j 被改为 19 了,而且这个 j 是函数的命名返回值,因此会影响最终函数的返回值。甚至这样写,函数的返回值依然是 19:

func hello(i *int) (j int) {
  defer func() {
    j = 19
  }()
  return *i
}

当然,通过汇编也可以看到这两者的写法的区别。但其实大家只需要知道,命名返回值,就好比函数参数一样,函数体内对命名返回值的任何修改,都会影响它。而非命名返回值,取决于 return 时候的值。

看似一道简单的题目,但却有迷惑性,因此需要大家对原理有所掌握。


往期推荐

我是 polarisxu,北大硕士毕业,曾在 360 等知名互联网公司工作,10多年技术研发与架构经验!2012 年接触 Go 语言并创建了 Go 语言中文网!著有《Go语言编程之旅》、开源图书《Go语言标准库》等。

坚持输出技术(包括 Go、Rust 等技术)、职场心得和创业感悟!欢迎关注「polarisxu」一起成长!也欢迎加我微信好友交流:gopherstudio

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值