golang defer语句是在return语句之前还是之后执行?

时间:2018-10-09 09:53:58

标签: go

我对golang defer有疑问: golang defer语句在return语句之前还是之后执行?

我已阅读Defer_statements。但是我没有答案。

我做了一个简单的测试:

func test1() (x int) {
    defer fmt.Printf("in defer: x = %d\n", x)

    x = 7
    return 9
}

func test2() (x int) {
    defer func() {
        fmt.Printf("in defer: x = %d\n", x)
    }()

    x = 7
    return 9
}

func test3() (x int) {
    x = 7
    defer fmt.Printf("in defer: x = %d\n", x)
    return 9
}

func main() {
    fmt.Println("test1")
    fmt.Printf("in main: x = %d\n", test1())
    fmt.Println("test2")
    fmt.Printf("in main: x = %d\n", test2())
    fmt.Println("test3")
    fmt.Printf("in main: x = %d\n", test3())
}

test1()中,使用Printf在延后打印x。 在test2()中,使用匿名函数在延迟后打印x。 在test3()中,使用Printf在延迟后打印x,但在x = 7之后延迟x。

但是结果是:

test1
in defer: x = 0
in main: x = 9
test2
in defer: x = 9
in main: x = 9
test3
in defer: x = 7
in main: x = 9

那么,有谁能解释: 1.为什么会得到这个结果?为什么test1打印0,test2打印9,test3打印7。 2.延迟陈述是在归还之后还是归还之前执行?

非常感谢。

5 个答案:

答案 0 :(得分:2)

感谢@ dev.bmax @Tranvu Xuannhat @ rb16。在您的帮助下,我从Defer_statements找到了关键的解释。

  

每次执行“ defer”语句时,都会照常评估调用的函数值和参数并重新保存,但不会调用实际函数。

我们可以将defer ...分成三部分。

  1. 调用延迟,评估函数参数的值。
  2. 执行延迟,将函数推入堆栈。
  3. 在返回或出现紧急情况后执行堆栈中的功能

我做了一个新的test4来解释。

func test4() (x int) {
    defer func(n int) {
        fmt.Printf("in defer x as parameter: x = %d\n", n)
        fmt.Printf("in defer x after return: x = %d\n", x)
    }(x)

    x = 7
    return 9
}

在test4中,

  1. 调用延迟,评估 n 的值, n = x = 0 ,因此x作为参数为0。
  2. 执行延迟,将func(n int)(0)推入堆栈。
  3. func(n int)(0)之后执行return 9,已评估fmt.Printf("in defer x as parameter: x = %d\n", n)中的n,现在将评估fmt.Printf("in defer x after return: x = %d\n", x)中的x,x为9。

所以,得到了结果:

test4
in defer x as parameter: x = 0
in defer x after return: x = 9
in main: x = 9

答案 1 :(得分:1)

first 测试中,当运行包含x的行时,将评估参数defer的值。当x仍为0时,它发生在第1行。

请注意,即使稍后会调用fmt.Printf,也会提前评估其参数。

第三测试中,在x语句运行之前,参数defer的值设置为7。同样,这发生在实际调用fmt.Printf之前。

第二测试有点不同。变量x在匿名函数的范围内。它的值在函数运行时进行评估,这在测试函数返回时发生。到那时x是9。

答案 2 :(得分:1)

我将参考Defer_statements来解释您的结果。

问题2:在返回后推迟执行。

问题1:来自文档

  

每次执行“ defer”语句时,都会照常评估调用的函数值和参数并重新保存,但不会调用实际函数。

  • 测试1:Defer调用fmt.Println。 fmt.Println评估x的值,该值为0(零值)。 test1()在主函数中返回9。

  • 测试2:Defer调用func(){}。 fmt.Println仅在执行func(){}时(返回之后)评估x。因此,x的值为9。test2()在主函数中返回9。

  • 测试3:Defer调用fmt.Println。 fmt.Println评估x的值为7(x分配给7)。 test3()在主函数中返回9。

答案 3 :(得分:0)

不是在此之前还是之后,这完全取决于defer语句是否存在于堆栈中。 如果是这样(如果控件到达defer语句,则将store语句保存到堆栈中),然后在return语句之后执行。

例如-

func testing() err {
    x, err := doSomething();
    if err != nil {
       return;
    }
    defer fmt.Println("hello")
    return nil
}

因此,如果err != nil永远不会执行,因为程序在控件到达defer语句之前就返回了。

现在来回答您的问题(所有内容都会提交到defer语句) 1.情况下,存储在堆栈中的语句为defer fmt.Printf("in defer: x = %d\n", x)(x的值为零)

  1. case-存储在堆栈中的语句是一个函数

defer func() { fmt.Printf("in defer: x = %d\n", x) }()

哪个依赖于x(在堆栈中请注意defer语句是一个函数,并且函数的值将在函数执行时分配)

  1. 案例-defer fmt.Printf("in defer: x = %d\n", x)的值为7

答案 4 :(得分:0)

package main

import "fmt"

func deque(a *[]int) {
    *a = (*a)[1:]
}

func test_defer(a* []int) int{
    defer deque(a)

    return (*a)[0]
}

func main() {
    a := []int{1,2}
    fmt.Println(test_defer(&a))
    fmt.Println(a)
}

尝试此代码

go run test.go 
1
[2]

test_defer返回1,因此在返回后执行延期