V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
trzzzz
V2EX  ›  Go 编程语言

请教 defer 的语法问题

  •  
  •   trzzzz · 130 天前 · 1609 次点击
    这是一个创建于 130 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    代码 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

    13 条回复    2024-07-20 10:42:08 +08:00
    Asakijz
        1
    Asakijz  
       130 天前   ❤️ 1
    可以参考下我之前提问的
    https://www.v2ex.com/t/868146#reply11
    SimbaPeng
        2
    SimbaPeng  
       130 天前
    a 的求值时间
    sduoduo233
        3
    sduoduo233  
       130 天前 via Android
    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
        4
    dilu  
       130 天前 via Android
    斗胆分析一下,第一种写法明显是在 func 结构体引用了一个外部变量,典型的闭包问题。而第二种写法的 defer 只是一个表达式,编译后只是一个简单的 funval 而没有对应的捕获列表。第一种写法在主函数执行完开始执行 defer 函数的时候,加载了这个 func 的 funcval 和捕获列表,其中对于 a 的引用是地址的形式,此时 a 是 10 故而打印了 10 。
    extrem
        5
    extrem  
       130 天前
    defer 语句将函数的执行推迟到周围的函数返回。

    延迟调用的参数会立即计算,但在周围的函数返回之前不会执行函数调用。
    dobelee
        6
    dobelee  
       130 天前
    存在传入局部变量时都用匿名函数。
    一般只有简单的关闭操作才会直接写表达式,比如
    defer x.Close()
    defer cancel()
    trzzzz
        7
    trzzzz  
    OP
       130 天前
    @Asakijz 感谢
    0x90200
        8
    0x90200  
       130 天前
    supuwoerc
        9
    supuwoerc  
       130 天前
    这是一个闭包吧~
    supuwoerc
        10
    supuwoerc  
       130 天前
    找了一下我当初写的 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
        11
    wanminny  
       129 天前
    defer 语句将函数的执行推迟到周围的函数返回。

    延迟调用的参数会立即计算,但在周围的函数返回之前不会执行函数调用。
    wswch4444
        12
    wswch4444  
       129 天前
    关键区别就在
    defer func() {
    fmt.Println(a)
    }()


    defer fmt.Println(a)



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


    用了闭包 是在 函数执行完 之后 给 a 赋值 所以是 11
    dyllen
        13
    dyllen  
       121 天前
    defer 相当于把一个函数放到栈里面稍后执行,函数参数会先计算好结果再放入栈。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5589 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 08:55 · PVG 16:55 · LAX 00:55 · JFK 03:55
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.