defer
语句
Go语言中的defer
语句会将其后面跟随的函数推迟到外层函数返回之后执行。推迟调用的函数其参数会立即求值,但直到外层函数返回前该函数都不会被调用。推迟的函数调用会被压入一个栈中,当外层函数返回时,被推迟的函数会按照后进先出的顺序调用。
defer
语句后面只能跟函数调用。
示例:
a := 1
b := 2
defer fmt.Println(1, a + b) // 推迟的函数参数会立即赋值,后续改变参数值不会影响结果
defer func() { // 该函数没有参数,如果函数内部使用了外层函数的变量,那么外层函数改变变量值会对结果产生影响
fmt.Println(2, a) // 这里 a == 2
}()
a = 2
b = 3
defer fmt.Println(3, a + b) // 后推迟的语句先输出结果
fmt.Println("end")
输出:
end
3 5
2 2
1 3
由于defer
语句延迟调用的特性,所以defer
语句能非常方便的处理资源释放问题。比如:资源清理、文件关闭、解锁及记录时间等。
defer
执行时机
- 对返回值进行赋值
- 执行
defer
语句 - 执行RET指令(函数返回)
这里需要区分一下函数返回值命名和不命名的情况。当返回值命名时,defer
语句可以对返回值进行修改,在判断返回值时需要注意defer
语句;当返回值没有命名时,defer
语句不会影响返回值。
示例:
func test1() int { // 返回值无命名
x := 300
defer func() {
x = 100 // 此时x=300早就赋值给了返回值,这里只是改变x的值,与返回值无关
}()
return x
}
func test2() (x int) { //返回值有命名
x = 300
defer func() {
x = 100 // 这里的x就是返回值,改变x就是直接改变返回值,如果改为x++,返回值为301
}()
return
}
func test3() (x int) {
x = 300
defer func(x int) {
x = 100 // 这里的x作为函数的参数,与返回值x不是同一个参数,所以对返回值没有影响
}(x)
return
}
func main() {
fmt.Println(test1()) // 300
fmt.Println(test2()) // 100
fmt.Println(test3()) // 300
}
面试题
func calc(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret)
return ret
}
func main() {
x := 1
y := 2
defer calc("AA", x, calc("A", x, y))
x = 10
defer calc("BB", x, calc("B", x, y))
y = 20
}
输出:
A 1 2 3
B 10 2 12
BB 10 12 22
AA 1 3 4
因为defer
语句推迟的函数的参数会立即求值,所以第一句defer
语句先计算x
和calc("A", x, y)
,传入的x = 1, y = 2
,第二句同理,只是传入的x
的值变成了10。
参考1、参考2、参考3