Golang Go语言中 defer、return、返回值之间执行顺序,想不通哇,求大哥哥们帮帮忙。

发布于 1周前 作者 ionicwang 来自 Go语言
func a() {
	i := 0
	i++
	//defer 执行阶段处于 return 之后,函数返回之前
	defer fmt.Println("defer", i) //那这个为什么输出为 1 ?不应该是 2 吗
	i++
	fmt.Println("i=", i)
	return
}

这是为啥子呢?


Golang Go语言中 defer、return、返回值之间执行顺序,想不通哇,求大哥哥们帮帮忙。
21 回复

https://golang.org/doc/effective_go.html#defer

> The arguments to the deferred function (which include the receiver if the function is a method) are evaluated when the defer executes, not when the call executes.

更多关于Golang Go语言中 defer、return、返回值之间执行顺序,想不通哇,求大哥哥们帮帮忙。的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


遇到 defer 就把后面的语句压栈,等执行完了,再执行栈中的语句,类似 c++栈对象的析构

defer 当然是取当前环境的变量了。
你想想,比如你开个循环读取 10 个文件,把关闭全部 defer 了,函数退出的时候是关闭这 10 个文件,还是把最后那个文件关闭 10 次?

之前写过一篇 blog,希望能帮到你

《 Golang 中 defer 的那些事》 https://xiaozhou.net/something-about-defer-2014-05-25.html

楼上的老哥们。大嘎好,我系渣渣灰。

关于 append1 中的问题,请参考: https://blog.golang.org/defer-panic-and-recover 中 3. Deferred functions may read and assign to the returning function’s named return values. 这一段。

That’s a feature.

不是因为 0 加 1 等于 1 吗。。。这个例子没体现 defer…

函数是值传递的,你怎么操作原来的值,拷贝都不会变啊!

一直疑惑,defer 的栈帧是寄存在什么地方的,和普通函数一样?

go<br>func b() (ret int) {<br> ret = 1<br> defer func() {<br> ret++<br> fmt.Printf("defer %d\n", ret)<br> }()<br> return 6<br>}<br>
在 return 6 这条语句执行时其实是先把 6 赋值给 ret,然后执行 defer,再设置 RET 标记,然后 return,所以输出 7。

多谢解惑!

defer 后直接调用函数时,参数是值传递的,所以在第一个例子中,i 的值就已经被固定为 1 了

但是在调用闭包时,无论是否 defer,只要变量不是通过参数列表传递给闭包,而是通过闭包的自动捕获变量拿到了这个变量,效果都是直接引用了这个变量本身
在第二个例子中的顺序就是
1: 为 ret 赋值为 6
2: 执行 defer 中的闭包,ret 被赋值为 7 并输出
3: b() 函数返回

如果第二个例子是如下这样的话,在 }(ret) 这一行,形参就已经被确定了,就会 defer 输出 1,函数返回 6

func b() (ret int) {
ret = 1
defer func(x int) {
x++
fmt.Printf(“defer %d\n”, x)
}(ret)
return 6
}

defer 输出 2 打错了

正好借楼问一句,我是 scala 和 java 那边过来在自己研究 fabric,然后你们有没有觉得 go 对 error handler 用起来特别麻烦呢?调用什么 api 都会返回 2 个参数,一个实际值,一个 err (比如 x,err=f()),然后每次都要写一段 if (err != nil) ,完全不如 java 那边直接 try catch 所有传递的 exception 来的简单方便。。。。

你可以试试直接 panic,然后外层调用者捕捉 panic,err 校验和 exception 也是各有优劣吧。

这让我感觉 C++的 lambda 中显式指定 capture 的做法真是无比英明的设计……

牛逼!我整明白了!再次表示感谢!

在Go语言中,deferreturn和返回值之间的执行顺序确实是一个常见的疑惑点,但理解其机制后就会豁然开朗。

首先,要明确defer语句会将其包裹的代码延迟到包含该defer的函数即将返回时才执行。这意味着,无论函数中有多少defer,它们都会在函数实际返回之前按后进先出的顺序(栈的顺序)执行。

return语句被执行时,Go会先计算返回值的表达式,但此时并不会立即返回给调用者。紧接着,如果有defer语句,它们会依次被执行。重要的是,如果defer中修改了命名返回值,这些修改会反映到最终的返回值上。

举个例子:

func example() (x int) {
    defer func() { x++ }()
    return 1
}

在这个例子中,return 1会先计算返回值x为1,但不会立即返回。随后defer中的匿名函数被执行,将x增加1。因此,最终example函数返回的值是2。

总结来说,Go语言中的执行顺序是:先计算return的返回值,然后执行所有defer语句(可能修改返回值),最后才将控制权返回给调用者。理解这一点,对编写健壮的Go代码至关重要。希望这能帮助你解开疑惑!

回到顶部