Defer 总结
虽说比较常用,但是遇到一道面试题还是比较懵逼,总结一下~
面试题
下面代码输出什么?
package main
import "fmt"
func calc(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret)
return ret
}
func main() {
a := 1
b := 2
defer calc("1", a, calc("10", a, b))
a = 0
defer calc("2", a, calc("20", a, b))
b = 1
}
/* output
10 1 2 3
20 0 2 2
2 0 2 2
1 1 3 4
*/
关键知识点
- defer 是什么?
defer 是 Go 语言提供的一种用于注册延迟调用的机制,每一次 defer 都会把函数压入栈中,当前函数返回前再把延迟函数取出并执行。
- 两种使用方式:
- 函数参数:作为函数参数,则在 defer 定义时就把值传递给 defer,并被缓存起来;
- 作为闭包引用:作为闭包引用的话,则会在 defer 函数真正调用时根据整个上下文确定当前的值。
- 核心:在 defer 语句里会不会改变返回值。
- return 不是原子操作:
- 赋值: 返回值 = xxx
- 空的 return defer 在 步骤 1 和 2 中间执行
- defer 进栈之前 执行参数(函数):defer 语句执行的时候,会直接得到所有参数,然后 defer 带着评估后的参数入栈
分析上边函数执行
- 根据知识点 4,先执行
calc("10", a, b) // a=1,b=2, 输出:10,1,2,3 并且 3 进入 defer 函数中
calc("20", a, b) // a=0,b=2, 输出:20,0,2,2 并且 2 进入 defer 函数中
- 先进后出
calc("2",a,2) // a=0 输出 2,0,2,2
calc("1", a,3)//a=1 输出 1,1,3,4
- 最终输出就定下来了
第二道 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("触发异常")
}
output:
打印后
打印中
打印前
panic: 触发异常
知识点
- panic 异常,程序中断运行,并立即执行当前 goroutine 中 defer 函数,随后程序崩溃输出日志信息:panic
fmt.Println()
及所有的 fmt 包输出,都是到 标准输出中os.Stdout
- Go 中 os.Stdout 是无缓冲的
- 无缓冲:没有缓冲区,数据会立即读入或者输出到外存文件和设备上。标准错误 stderr 是无缓冲的,这样保证错误提示和输出能够及时反馈给用户,供用户排除错误。
参考
- 原文作者:战神西红柿
- 原文链接:https://tomatoares.github.io/posts/go/defer/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。