defer的定义

Go’s defer statement schedules a function call (the deferred function) to be run immediately before the function executing the defer returns. It’s an unusual but effective way to deal with situations such as resources that must be released regardless of which path a function takes to return.

Go的defer语句声明一个在函数执行defer返回之前立即运行的函数调用(延迟函数)。这是一个不同寻常的但有效的方法去处理资源无论如何都必須释放在函数返回的路徑上。

注意事项

面试题

1.下面的代码执行结果

go
 1package main
 2
 3import "log"
 4
 5func foo(n int) int {
 6    defer n++
 7    return n
 8}
 9
10func main() {
11    var i int = 100
12    foo(i)
13}

考点:defer 后面必须跟函数调用,不能是其它语句

运行结果:

./prog.go:6:11: expression in defer must be function call
./prog.go:6:12: syntax error: unexpected ++ at end of statement

2. 写出下面代码输出内容

go
 1package main
 2
 3import("fmt") 
 4
 5func main() {
 6    defer_call()
 7}
 8
 9func defer_call() {
10    defer func() {
11        fmt.Println("打印前")
12    }()
13    defer func() {
14        fmt.Println("打印中")
15    }() 
16    defer func() {
17        fmt.Println("打印后")
18    }() 
19    panic("触发异常")
20}

考点:defer执行顺序

解答: defer 是后进先出。 panic 需要等defer 结束后才会向上传递。出现panic恐慌时候,会先按照defer的后入先出的顺序执行,最后才会执行panic。

运行结果:

打印后
打印中
打印前
panic: 触发异常

3. 下面代码输出什么?

go
 1func calc(index string, a, b int) int {
 2    ret := a + b 
 3    fmt.Println(index, a, b, ret) 
 4    return ret
 5}
 6
 7func main() {
 8    a := 1
 9    b := 2 
10    defer calc("1", a, calc("10", a, b))
11    a = 0 
12    defer calc("2", a, calc("20", a, b)) 
13    b = 1
14}

考点:defer函数参数的计算时间点

解答:这道题类似第2题需要注意到defer执行顺序和值传递 index:1肯定是最后执行的,但是index:1的第三个参数是一个函数,所以最先被调用calc(“10”,1,2)==>10,1,2,3 执行index:2时,与之前一样,需要先调用calc(“20”,0,2)==>20,0,2,2 执行到b=1时候开始调用,index:2==>calc(“2”,0,2)==>2,0,2,2 最后执行index:1==>calc(“1”,1,3)==>1,1,3,4

运行结果:

10 1 2 3
20 0 2 2
2 0 2 2
1 1 3 4

4. 思考下面代码的输出

go
 1package main
 2
 3import "log"
 4
 5func foo1(i *int) int {
 6    *i += 100
 7    defer func() { *i += 200 }()
 8    log.Printf("i=%d", *i)
 9    return *i
10}
11
12func foo2(i *int) (r int) {
13    *i += 100
14    defer func() { r += 200 }()
15    log.Printf("i=%d", *i)
16    return *i
17}
18
19func main() {
20    var i, r int
21
22    i,r = 0,0
23    r = foo1(&i)
24    log.Printf("i=%d, r=%d\n", i, r)
25
26    i,r = 0,0
27    r = foo2(&i)
28    log.Printf("i=%d, r=%d\n", i, r)
29}

考点:defer函数会影响宿主函数的返回值

解答:这个例子其实有一点拗口的。 foo1 return指令前(i==100, ret==0),return指令后(i==100, ret=100),然后调用defer函数后(i==300,r==100),defer函数增加了i;main函数收到(i==300, r==100)
foo2 return指令前(i==100, ret==0),return指令后(i==100, ret=100),然后调用defer函数后(i==100,r==300),defer函数增加了ret;main函数收到(i==100, r==300)

运行结果:

i=100
i=300, r=100
i=100
i=100, r=300