defer语句的工作原理是怎样的?

huangapple go评论82阅读模式
英文:

how defer statement works exactly

问题

我试着学习Go语言,并使用《Effective Go》作为教材。我在使用defer语句时遇到了困难,看看下面的代码:

package main

import "fmt"

func trace(s string) string {
    fmt.Println("entering:", s)
    return s
}

func un(s string) {
    fmt.Println("leaving:", s)
}

func a() {
    defer un(trace("a"))
    fmt.Println("in a")
}

func b() {
    defer un(trace("b"))
    fmt.Println("in b")
    a()
}

func main() {
    b()
}

输出结果是:

entering: b
in b
entering: a
in a
leaving: a
leaving: b

我知道,defer语句会在函数中的return语句之后执行。但是在这里,为什么"entering: b"是第一个输出?我期望"entering: b"是第一个输出!

英文:

i try to learn golang and use effective go as lecture. I stuck on capital defer, look at the following code

package main

import "fmt"

func trace(s string) string {
	fmt.Println("entering:", s)
	return s
}

func un(s string) {
	fmt.Println("leaving:", s)
}

func a() {
	defer un(trace("a"))
	fmt.Println("in a")
}

func b() {
	defer un(trace("b"))
	fmt.Println("in b")
	a()
}

func main() {
	b()
}

as output i've got

entering: b
in b
entering: a
in a
leaving: a
leaving: b

I know, that defer statement will be execute after return statement in the function. But here, why entering: b is the first output? I am expected in b as the fist output!

答案1

得分: 8

根据http://golang.org/doc/effective_go.html#defer的说明:

延迟函数的参数(如果函数是一个方法,则包括接收者)在延迟执行时进行求值,而不是在调用执行时进行求值。除了避免函数执行过程中变量值的变化之外,这意味着一个延迟调用点可以延迟多个函数的执行。

因此,在你的情况下,defer函数的参数(trace("b"))首先进行求值。

英文:

According to http://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. Besides avoiding worries about variables changing values as the function executes, this means that a single deferred call site can defer multiple function executions.

So argument of defer function (trace("b") in your case) evaluates first

答案2

得分: 2

简化后的代码如下:

package main

import "fmt"

func trace(s string) string {
    fmt.Println("进入:", s)
    return s
}

func un(s string) {
    fmt.Println("离开:", s)
}

func b() {
    defer un(trace("b"))
    fmt.Println("在 b 中")
}

func main() {
    b()
}

输出结果:

进入: b
在 b 中
离开: b

由于"延迟函数的参数在 defer 语句执行时被求值",所以在函数 b 中的语句

defer un(trace("b"))

可以等价地写为

{
    unarg := trace("b")
    defer un(unarg)
}

因此,等价的代码如下:

package main

import "fmt"

func trace(s string) string {
    fmt.Println("进入:", s)
    return s
}

func un(s string) {
    fmt.Println("离开:", s)
}

func b() {
    {
        unarg := trace("b")
        defer un(unarg)
    }
    fmt.Println("在 b 中")
}

func main() {
    b()
}

输出结果:

进入: b
在 b 中
离开: b

同样地,对于完整的示例代码:

package main

import "fmt"

func trace(s string) string {
    fmt.Println("进入:", s)
    return s
}

func un(s string) {
    fmt.Println("离开:", s)
}

func a() {
    {
        unarg := trace("a")
        defer un(unarg)
    }
    fmt.Println("在 a 中")
}

func b() {
    {
        unarg := trace("b")
        defer un(unarg)
    }
    fmt.Println("在 b 中")
    a()
}

func main() {
    b()
}

输出结果:

进入: b
在 b 中
进入: a
在 a 中
离开: a
离开: b
英文:

Simplifying,

package main

import "fmt"

func trace(s string) string {
    fmt.Println("entering:", s)
    return s
}

func un(s string) {
    fmt.Println("leaving:", s)
}

func b() {
    defer un(trace("b"))
    fmt.Println("in b")
}

func main() {
    b()
}

Output:

entering: b
in b
leaving: b

Since "arguments to deferred functions are evaluated when the defer executes", the

defer un(trace("b"))

statement in function b is, using explicit scope, equivalent to

{
	unarg := trace("b")
	defer un(unarg)
}

Therefore, equivalently,

package main

import "fmt"

func trace(s string) string {
	fmt.Println("entering:", s)
	return s
}

func un(s string) {
	fmt.Println("leaving:", s)
}

func b() {
	{
		unarg := trace("b")
		defer un(unarg)
	}
	fmt.Println("in b")
}

func main() {
	b()
}

Output:

entering: b
in b
leaving: b

Equivalently, for your complete example,

package main

import "fmt"

func trace(s string) string {
	fmt.Println("entering:", s)
	return s
}

func un(s string) {
	fmt.Println("leaving:", s)
}

func a() {
	{
		unarg := trace("a")
		defer un(unarg)
	}
	fmt.Println("in a")
}

func b() {
	{
		unarg := trace("b")
		defer un(unarg)
	}
	fmt.Println("in b")
	a()
}

func main() {
	b()
}

Output:

entering: b
in b
entering: a
in a
leaving: a
leaving: b

huangapple
  • 本文由 发表于 2014年7月16日 16:00:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/24775306.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定