关于defer行为的Go语言面试题
Go language interview question about defer behavior
我有 2 个代码示例:
func test() int {
var x int
defer func() {
x++
}()
x = 1
return x
}
func main() {
fmt.Println(test())
}
它returns:1
。但是,以下代码示例的行为有所不同:
func test() (x int) {
defer func() {
x++
}()
x = 1
return
}
func main() {
fmt.Println(test())
}
它returns 2
.
为了搞清楚是怎么回事,我反汇编了代码
第一个代码片段(输出为1
):
main.go:19 0x48cf40 64488b0c25f8ffffff MOVQ FS:0xfffffff8, CX
main.go:19 0x48cf49 483b6110 CMPQ 0x10(CX), SP
main.go:19 0x48cf4d 7678 JBE 0x48cfc7
main.go:19 0x48cf4f 4883ec58 SUBQ [=12=]x58, SP
main.go:19 0x48cf53 48896c2450 MOVQ BP, 0x50(SP)
main.go:19 0x48cf58 488d6c2450 LEAQ 0x50(SP), BP
main.go:20 0x48cf5d e83effffff CALL main.test(SB)
main.go:20 0x48cf62 e8c9bff7ff CALL runtime.convT64(SB)
main.go:20 0x48cf67 488b442408 MOVQ 0x8(SP), AX
main.go:20 0x48cf6c 0f57c0 XORPS X0, X0
main.go:20 0x48cf6f 0f11442440 MOVUPS X0, 0x40(SP)
main.go:20 0x48cf74 488d0d05090100 LEAQ 0x10905(IP), CX
main.go:20 0x48cf7b 48894c2440 MOVQ CX, 0x40(SP)
main.go:20 0x48cf80 4889442448 MOVQ AX, 0x48(SP)
print.go:274 0x48cf85 488b0524020d00 MOVQ os.Stdout(SB), AX
print.go:274 0x48cf8c 488d0d0dd70400 LEAQ go.itab.*os.File,io.Writer(SB), CX
print.go:274 0x48cf93 48890c24 MOVQ CX, 0(SP)
print.go:274 0x48cf97 4889442408 MOVQ AX, 0x8(SP)
print.go:274 0x48cf9c 488d442440 LEAQ 0x40(SP), AX
print.go:274 0x48cfa1 4889442410 MOVQ AX, 0x10(SP)
print.go:274 0x48cfa6 48c744241801000000 MOVQ [=12=]x1, 0x18(SP)
print.go:274 0x48cfaf 48c744242001000000 MOVQ [=12=]x1, 0x20(SP)
print.go:274 0x48cfb8 e84397ffff CALL fmt.Fprintln(SB)
print.go:274 0x48cfbd 488b6c2450 MOVQ 0x50(SP), BP
print.go:274 0x48cfc2 4883c458 ADDQ [=12=]x58, SP
print.go:274 0x48cfc6 c3 RET
main.go:19 0x48cfc7 e8d447fcff CALL runtime.morestack_noctxt(SB)
main.go:19 0x48cfcc e96fffffff JMP main.main(SB)
对于第二个代码片段(其输出为2
):
main.go:18 0x48cf30 64488b0c25f8ffffff MOVQ FS:0xfffffff8, CX
main.go:18 0x48cf39 483b6110 CMPQ 0x10(CX), SP
main.go:18 0x48cf3d 7678 JBE 0x48cfb7
main.go:18 0x48cf3f 4883ec58 SUBQ [=13=]x58, SP
main.go:18 0x48cf43 48896c2450 MOVQ BP, 0x50(SP)
main.go:18 0x48cf48 488d6c2450 LEAQ 0x50(SP), BP
main.go:19 0x48cf4d e84effffff CALL main.test(SB)
main.go:19 0x48cf52 e8d9bff7ff CALL runtime.convT64(SB)
main.go:19 0x48cf57 488b442408 MOVQ 0x8(SP), AX
main.go:19 0x48cf5c 0f57c0 XORPS X0, X0
main.go:19 0x48cf5f 0f11442440 MOVUPS X0, 0x40(SP)
main.go:19 0x48cf64 488d0d15090100 LEAQ 0x10915(IP), CX
main.go:19 0x48cf6b 48894c2440 MOVQ CX, 0x40(SP)
main.go:19 0x48cf70 4889442448 MOVQ AX, 0x48(SP)
print.go:274 0x48cf75 488b0534020d00 MOVQ os.Stdout(SB), AX
print.go:274 0x48cf7c 488d0d1dd70400 LEAQ go.itab.*os.File,io.Writer(SB), CX
print.go:274 0x48cf83 48890c24 MOVQ CX, 0(SP)
print.go:274 0x48cf87 4889442408 MOVQ AX, 0x8(SP)
print.go:274 0x48cf8c 488d442440 LEAQ 0x40(SP), AX
print.go:274 0x48cf91 4889442410 MOVQ AX, 0x10(SP)
print.go:274 0x48cf96 48c744241801000000 MOVQ [=13=]x1, 0x18(SP)
print.go:274 0x48cf9f 48c744242001000000 MOVQ [=13=]x1, 0x20(SP)
print.go:274 0x48cfa8 e85397ffff CALL fmt.Fprintln(SB)
print.go:274 0x48cfad 488b6c2450 MOVQ 0x50(SP), BP
print.go:274 0x48cfb2 4883c458 ADDQ [=13=]x58, SP
print.go:274 0x48cfb6 c3 RET
main.go:18 0x48cfb7 e8e447fcff CALL runtime.morestack_noctxt(SB)
main.go:18 0x48cfbc e96fffffff JMP main.main(SB)
Golang 规范说 this 关于 defer 语句:
[...] if the deferred function is a function literal and the surrounding function has named result parameters that are in scope within the literal, the deferred function may access and modify the result parameters before they are returned.
(我的重点)
在your first snippet中,函数test
没有命名的return参数; x
只是一个局部变量。因此,您的 defer 语句不能修改函数 test
.
的结果
在 your second snippet 中,函数 test
有一个命名的 return 参数 x
,它在您的函数文字范围内。因此,defer 语句可以(并且确实)修改函数 test
.
的结果
我有 2 个代码示例:
func test() int {
var x int
defer func() {
x++
}()
x = 1
return x
}
func main() {
fmt.Println(test())
}
它returns:1
。但是,以下代码示例的行为有所不同:
func test() (x int) {
defer func() {
x++
}()
x = 1
return
}
func main() {
fmt.Println(test())
}
它returns 2
.
为了搞清楚是怎么回事,我反汇编了代码
第一个代码片段(输出为1
):
main.go:19 0x48cf40 64488b0c25f8ffffff MOVQ FS:0xfffffff8, CX
main.go:19 0x48cf49 483b6110 CMPQ 0x10(CX), SP
main.go:19 0x48cf4d 7678 JBE 0x48cfc7
main.go:19 0x48cf4f 4883ec58 SUBQ [=12=]x58, SP
main.go:19 0x48cf53 48896c2450 MOVQ BP, 0x50(SP)
main.go:19 0x48cf58 488d6c2450 LEAQ 0x50(SP), BP
main.go:20 0x48cf5d e83effffff CALL main.test(SB)
main.go:20 0x48cf62 e8c9bff7ff CALL runtime.convT64(SB)
main.go:20 0x48cf67 488b442408 MOVQ 0x8(SP), AX
main.go:20 0x48cf6c 0f57c0 XORPS X0, X0
main.go:20 0x48cf6f 0f11442440 MOVUPS X0, 0x40(SP)
main.go:20 0x48cf74 488d0d05090100 LEAQ 0x10905(IP), CX
main.go:20 0x48cf7b 48894c2440 MOVQ CX, 0x40(SP)
main.go:20 0x48cf80 4889442448 MOVQ AX, 0x48(SP)
print.go:274 0x48cf85 488b0524020d00 MOVQ os.Stdout(SB), AX
print.go:274 0x48cf8c 488d0d0dd70400 LEAQ go.itab.*os.File,io.Writer(SB), CX
print.go:274 0x48cf93 48890c24 MOVQ CX, 0(SP)
print.go:274 0x48cf97 4889442408 MOVQ AX, 0x8(SP)
print.go:274 0x48cf9c 488d442440 LEAQ 0x40(SP), AX
print.go:274 0x48cfa1 4889442410 MOVQ AX, 0x10(SP)
print.go:274 0x48cfa6 48c744241801000000 MOVQ [=12=]x1, 0x18(SP)
print.go:274 0x48cfaf 48c744242001000000 MOVQ [=12=]x1, 0x20(SP)
print.go:274 0x48cfb8 e84397ffff CALL fmt.Fprintln(SB)
print.go:274 0x48cfbd 488b6c2450 MOVQ 0x50(SP), BP
print.go:274 0x48cfc2 4883c458 ADDQ [=12=]x58, SP
print.go:274 0x48cfc6 c3 RET
main.go:19 0x48cfc7 e8d447fcff CALL runtime.morestack_noctxt(SB)
main.go:19 0x48cfcc e96fffffff JMP main.main(SB)
对于第二个代码片段(其输出为2
):
main.go:18 0x48cf30 64488b0c25f8ffffff MOVQ FS:0xfffffff8, CX
main.go:18 0x48cf39 483b6110 CMPQ 0x10(CX), SP
main.go:18 0x48cf3d 7678 JBE 0x48cfb7
main.go:18 0x48cf3f 4883ec58 SUBQ [=13=]x58, SP
main.go:18 0x48cf43 48896c2450 MOVQ BP, 0x50(SP)
main.go:18 0x48cf48 488d6c2450 LEAQ 0x50(SP), BP
main.go:19 0x48cf4d e84effffff CALL main.test(SB)
main.go:19 0x48cf52 e8d9bff7ff CALL runtime.convT64(SB)
main.go:19 0x48cf57 488b442408 MOVQ 0x8(SP), AX
main.go:19 0x48cf5c 0f57c0 XORPS X0, X0
main.go:19 0x48cf5f 0f11442440 MOVUPS X0, 0x40(SP)
main.go:19 0x48cf64 488d0d15090100 LEAQ 0x10915(IP), CX
main.go:19 0x48cf6b 48894c2440 MOVQ CX, 0x40(SP)
main.go:19 0x48cf70 4889442448 MOVQ AX, 0x48(SP)
print.go:274 0x48cf75 488b0534020d00 MOVQ os.Stdout(SB), AX
print.go:274 0x48cf7c 488d0d1dd70400 LEAQ go.itab.*os.File,io.Writer(SB), CX
print.go:274 0x48cf83 48890c24 MOVQ CX, 0(SP)
print.go:274 0x48cf87 4889442408 MOVQ AX, 0x8(SP)
print.go:274 0x48cf8c 488d442440 LEAQ 0x40(SP), AX
print.go:274 0x48cf91 4889442410 MOVQ AX, 0x10(SP)
print.go:274 0x48cf96 48c744241801000000 MOVQ [=13=]x1, 0x18(SP)
print.go:274 0x48cf9f 48c744242001000000 MOVQ [=13=]x1, 0x20(SP)
print.go:274 0x48cfa8 e85397ffff CALL fmt.Fprintln(SB)
print.go:274 0x48cfad 488b6c2450 MOVQ 0x50(SP), BP
print.go:274 0x48cfb2 4883c458 ADDQ [=13=]x58, SP
print.go:274 0x48cfb6 c3 RET
main.go:18 0x48cfb7 e8e447fcff CALL runtime.morestack_noctxt(SB)
main.go:18 0x48cfbc e96fffffff JMP main.main(SB)
Golang 规范说 this 关于 defer 语句:
[...] if the deferred function is a function literal and the surrounding function has named result parameters that are in scope within the literal, the deferred function may access and modify the result parameters before they are returned.
(我的重点)
在your first snippet中,函数test
没有命名的return参数; x
只是一个局部变量。因此,您的 defer 语句不能修改函数 test
.
在 your second snippet 中,函数 test
有一个命名的 return 参数 x
,它在您的函数文字范围内。因此,defer 语句可以(并且确实)修改函数 test
.