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返回之前立即运行的函数调用(延迟函数)。这是一个不同寻常的但有效的方法去处理资源无论如何都必須释放在函数返回的路徑上。
注意事项 ¶
- defer后面必须是函数调用语句,不能是其他语句,否则编译器会出错
- 多个defer的执行顺序,defer的执行遵循先进后出原则,即先定义的defer后执行
- defer函数参数的计算时间点,defer函数的参数是在defer语句出现的位置做计算的,而不是在函数运行的时候做计算的,即所在函数结束的时候计算的
- defer函数会影响宿主函数的返回值
面试题 ¶
1.下面的代码执行结果 ¶
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. 写出下面代码输出内容 ¶
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. 下面代码输出什么? ¶
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. 思考下面代码的输出 ¶
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