Description
defer
和panic
的执行顺序
package main
import (
"fmt"
)
func main() {
defer_call()
}
func defer_call() {
defer func() { fmt.Println("打印前") }()
defer func() { fmt.Println("打印中") }()
defer func() { fmt.Println("打印后") }()
panic("触发异常")
}
输出:
打印后
打印中
打印前
panic: 触发异常
解析:defer
的执行顺序是后进先出。当出现 panic
语句的时候,会先按照 defer
的后进先出的顺序执行,最后才会执行 panic
。
- 下面代码输出什么
func increaseA() int {
var i int
defer func() {
i++
}()
return i
}
func increaseB() (r int) {
defer func() {
r++
}()
return r
}
func main() {
fmt.Println(increaseA())
fmt.Println(increaseB())
}
解析:increaseA()
的返回参数是匿名,increaseB()
是具名。所以结果是0, 1
。
- 下面代码输出什么
func f1() (r int) {
defer func() {
r++
}()
return 0
}
func f2() (r int) {
t := 5
defer func() {
t = t + 5
}()
return t
}
func f3() (r int) {
defer func(r int) {
r = r + 5
}(r)
return 1
}
解析:
f1()
结果为1
,拆解过程:
func f1() (r int) {
// 1.赋值
r = 0
// 2.闭包引用,返回值被修改
defer func() {
r++
}()
// 3.空的 return
return
}
f2()
结果为5
,拆解过程:
func f2() (r int) {
t := 5
// 1.赋值
r = t
// 2.闭包引用,但是没有修改返回值 r
defer func() {
t = t + 5
}()
// 3.空的 return
return
}
f3()
结果为1
,拆解过程:
func f3() (r int) {
// 1.赋值
r = 1
// 2.r 作为函数参数,不会修改要返回的那个 r 值
defer func(r int) {
r = r + 5
}(r)
// 3.空的 return
return
}
第2
步,r
是作为函数参数使用,是一份复制,defer
语句里面的 r
和 外面的 r
其实是两个变量,里面变量的改变不会影响外层变量 r
,所以不是返回 6
,而是返回 1
。
- 下面代码输出什么
type Person struct {
age int
}
func main() {
person := &Person{28}
// 1.
defer fmt.Println(person.age)
// 2.
defer func(p *Person) {
fmt.Println(p.age)
}(person)
// 3.
defer func() {
fmt.Println(person.age)
}()
person.age = 29
}
解析:
-
1
中将 28 当做defer
函数的参数,会把28
缓存在栈中,等到最后执行该defer
语句的时候取出,即输出28
; -
2
缓存的是结构体Person{28}
的地址,最终Person{28}
的age
被重新赋值为29
,所以defer
语句最后执行的时候,依靠缓存的地址取出的age
便是 29,即输出29
; -
3
中闭包引用,输出29
。
- 下面代码输出什么
func hello(i int) {
fmt.Println(i)
}
func main() {
i := 5
defer hello(i)
i = i + 10
}
解析:hello()
函数的参数在执行 defer
语句的时候会保存一份副本在实际调用 hello()
函数时用,所以是 5
。
return
之后的defer
语句会执行吗,下面这段代码输出什么?
var a bool = true
func main() {
defer func(){
fmt.Println("1")
}()
if a == true {
fmt.Println("2")
return
}
defer func(){
fmt.Println("3")
}()
}
解析:defer
关键字后面的函数或者方法想要执行必须先注册,return
之后的defer
是不能注册的, 也就不能执行后面的函数或方法。
- 下面这段代码输出什么
func f(n int) (r int) {
defer func() {
r += n
recover()
}()
var f func()
defer f()
f = func() {
r += 2
}
return n + 1
}
func main() {
fmt.Println(f(3))
}
输出:7
解析:第一步执行r = n + 1
,接着执行第二个 defer
,由于此时 f()
未定义,引发异常,随即执行第一个 defer,异常被
recover(),程序正常执行,最后
return`。
- 下面的代码输出什么
func F(n int) func() int {
return func() int {
n++
return n
}
}
func main() {
f := F(5)
defer func() {
fmt.Println(f())
}()
defer fmt.Println(f())
i := f()
fmt.Println(i)
}
输出:7 6 8
解析:defer
后面的函数如果带参数,会优先计算参数,并将结果存储在栈中,到真正执行 defer
的时候取出。
recover
的调用
func main() {
defer func() {
recover()
}()
panic(1)
}
recover()
必须在 defer()
函数中直接调用才有效。
那么下面代码输出什么
func main() {
defer func() {
fmt.Print(recover())
}()
defer func() {
defer fmt.Print(recover())
panic(1)
}()
defer recover()
panic(2)
}
defer recover()
行代码捕获是无效的,在调用 defer()
时,便会计算函数的参数并压入栈中,所以执行 defer fmt.Print(recover())
行代码时,此时便会捕获 panic(2)
。此后的 panic(1)
,会被上一层的 recover()
捕获,最终输出 2 1
。