请教 defer 的语法问题

2024-07-10 22:14:29 +08:00
 trzzzz

下面两个代码看起来差不多,但运行结果却天差地别

代码 1

		var a int
		defer func() {
			fmt.Println(a)
		}()
		for {
			if a > 10 {
				return
			}
			a++
		}

代码 2

		var a int
		defer fmt.Println(a)
		for {
			if a > 10 {
				return
			}
			a++
		}

结果:
代码 1 输出:11
代码 2 输出:0

2055 次点击
所在节点    Go 编程语言
13 条回复
Asakijz
2024-07-10 22:25:32 +08:00
可以参考下我之前提问的
https://ex.noerr.eu.org/t/868146#reply11
SimbaPeng
2024-07-10 22:31:08 +08:00
a 的求值时间
sduoduo233
2024-07-10 22:36:25 +08:00
defer 的参数会提前求值

A defer statement defers the execution of a function until the surrounding function returns.

The deferred call's arguments are evaluated immediately, but the function call is not executed until the surrounding function returns.
dilu
2024-07-10 22:38:21 +08:00
斗胆分析一下,第一种写法明显是在 func 结构体引用了一个外部变量,典型的闭包问题。而第二种写法的 defer 只是一个表达式,编译后只是一个简单的 funval 而没有对应的捕获列表。第一种写法在主函数执行完开始执行 defer 函数的时候,加载了这个 func 的 funcval 和捕获列表,其中对于 a 的引用是地址的形式,此时 a 是 10 故而打印了 10 。
extrem
2024-07-10 23:23:15 +08:00
defer 语句将函数的执行推迟到周围的函数返回。

延迟调用的参数会立即计算,但在周围的函数返回之前不会执行函数调用。
dobelee
2024-07-10 23:30:45 +08:00
存在传入局部变量时都用匿名函数。
一般只有简单的关闭操作才会直接写表达式,比如
defer x.Close()
defer cancel()
trzzzz
2024-07-11 08:54:38 +08:00
@Asakijz 感谢
0x90200
2024-07-11 09:27:54 +08:00
supuwoerc
2024-07-11 14:16:45 +08:00
这是一个闭包吧~
supuwoerc
2024-07-11 14:24:55 +08:00
找了一下我当初写的 Demo:
```go
package main

import "fmt"

func main() {
Demo1()
fmt.Println("----------")
Demo2()
fmt.Println("----------")
Demo3()
}

func Demo1() {
for i := 0; i < 3; i++ {
defer func() {
fmt.Println(i) // 每次循环的 i 都是同一个 i(demo1 函数作用域中的 i ,这个上下文中的 i 在函数执行前 i 已经自增加到 3)
}()
}
}

func Demo2() {
for i := 0; i < 3; i++ {
defer func(val int) {
fmt.Println(val) // 函数声明时的参数,并未产生闭包,正常执行
}(i)
}
}

func Demo3() {
for i := 0; i < 3; i++ {
k := i // 每次循环产生一个新的 k ,函数引用这个上下文中的 k ,后续的循环并不会修改每个上下文中的 k
defer func() {
fmt.Println(k)
}()
}
}
```
wanminny
2024-07-11 17:39:08 +08:00
defer 语句将函数的执行推迟到周围的函数返回。

延迟调用的参数会立即计算,但在周围的函数返回之前不会执行函数调用。
wswch4444
2024-07-11 17:39:25 +08:00
关键区别就在
defer func() {
fmt.Println(a)
}()


defer fmt.Println(a)



一个用了闭包,一个没用,没有用的,是在 编译到这个 defer 语句就确定了 a 的数值 零值 0


用了闭包 是在 函数执行完 之后 给 a 赋值 所以是 11
dyllen
2024-07-20 10:42:08 +08:00
defer 相当于把一个函数放到栈里面稍后执行,函数参数会先计算好结果再放入栈。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://ex.noerr.eu.org/t/1056374

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX