关于延迟行为的Go语言面试问题

yquaqz18  于 2023-10-14  发布在  Go
关注(0)|答案(1)|浏览(93)

我有两个代码示例:

  1. func test() int {
  2. var x int
  3. defer func() {
  4. x++
  5. }()
  6. x = 1
  7. return x
  8. }
  9. func main() {
  10. fmt.Println(test())
  11. }

返回:1。但是,下面的代码示例的行为有所不同:

  1. func test() (x int) {
  2. defer func() {
  3. x++
  4. }()
  5. x = 1
  6. return
  7. }
  8. func main() {
  9. fmt.Println(test())
  10. }

返回2
为了弄清楚到底发生了什么,我把代码反汇编了一下。
对于第一个代码片段(其输出为1):

  1. main.go:19 0x48cf40 64488b0c25f8ffffff MOVQ FS:0xfffffff8, CX
  2. main.go:19 0x48cf49 483b6110 CMPQ 0x10(CX), SP
  3. main.go:19 0x48cf4d 7678 JBE 0x48cfc7
  4. main.go:19 0x48cf4f 4883ec58 SUBQ $0x58, SP
  5. main.go:19 0x48cf53 48896c2450 MOVQ BP, 0x50(SP)
  6. main.go:19 0x48cf58 488d6c2450 LEAQ 0x50(SP), BP
  7. main.go:20 0x48cf5d e83effffff CALL main.test(SB)
  8. main.go:20 0x48cf62 e8c9bff7ff CALL runtime.convT64(SB)
  9. main.go:20 0x48cf67 488b442408 MOVQ 0x8(SP), AX
  10. main.go:20 0x48cf6c 0f57c0 XORPS X0, X0
  11. main.go:20 0x48cf6f 0f11442440 MOVUPS X0, 0x40(SP)
  12. main.go:20 0x48cf74 488d0d05090100 LEAQ 0x10905(IP), CX
  13. main.go:20 0x48cf7b 48894c2440 MOVQ CX, 0x40(SP)
  14. main.go:20 0x48cf80 4889442448 MOVQ AX, 0x48(SP)
  15. print.go:274 0x48cf85 488b0524020d00 MOVQ os.Stdout(SB), AX
  16. print.go:274 0x48cf8c 488d0d0dd70400 LEAQ go.itab.*os.File,io.Writer(SB), CX
  17. print.go:274 0x48cf93 48890c24 MOVQ CX, 0(SP)
  18. print.go:274 0x48cf97 4889442408 MOVQ AX, 0x8(SP)
  19. print.go:274 0x48cf9c 488d442440 LEAQ 0x40(SP), AX
  20. print.go:274 0x48cfa1 4889442410 MOVQ AX, 0x10(SP)
  21. print.go:274 0x48cfa6 48c744241801000000 MOVQ $0x1, 0x18(SP)
  22. print.go:274 0x48cfaf 48c744242001000000 MOVQ $0x1, 0x20(SP)
  23. print.go:274 0x48cfb8 e84397ffff CALL fmt.Fprintln(SB)
  24. print.go:274 0x48cfbd 488b6c2450 MOVQ 0x50(SP), BP
  25. print.go:274 0x48cfc2 4883c458 ADDQ $0x58, SP
  26. print.go:274 0x48cfc6 c3 RET
  27. main.go:19 0x48cfc7 e8d447fcff CALL runtime.morestack_noctxt(SB)
  28. main.go:19 0x48cfcc e96fffffff JMP main.main(SB)

对于第二个代码片段(其输出为2):

  1. main.go:18 0x48cf30 64488b0c25f8ffffff MOVQ FS:0xfffffff8, CX
  2. main.go:18 0x48cf39 483b6110 CMPQ 0x10(CX), SP
  3. main.go:18 0x48cf3d 7678 JBE 0x48cfb7
  4. main.go:18 0x48cf3f 4883ec58 SUBQ $0x58, SP
  5. main.go:18 0x48cf43 48896c2450 MOVQ BP, 0x50(SP)
  6. main.go:18 0x48cf48 488d6c2450 LEAQ 0x50(SP), BP
  7. main.go:19 0x48cf4d e84effffff CALL main.test(SB)
  8. main.go:19 0x48cf52 e8d9bff7ff CALL runtime.convT64(SB)
  9. main.go:19 0x48cf57 488b442408 MOVQ 0x8(SP), AX
  10. main.go:19 0x48cf5c 0f57c0 XORPS X0, X0
  11. main.go:19 0x48cf5f 0f11442440 MOVUPS X0, 0x40(SP)
  12. main.go:19 0x48cf64 488d0d15090100 LEAQ 0x10915(IP), CX
  13. main.go:19 0x48cf6b 48894c2440 MOVQ CX, 0x40(SP)
  14. main.go:19 0x48cf70 4889442448 MOVQ AX, 0x48(SP)
  15. print.go:274 0x48cf75 488b0534020d00 MOVQ os.Stdout(SB), AX
  16. print.go:274 0x48cf7c 488d0d1dd70400 LEAQ go.itab.*os.File,io.Writer(SB), CX
  17. print.go:274 0x48cf83 48890c24 MOVQ CX, 0(SP)
  18. print.go:274 0x48cf87 4889442408 MOVQ AX, 0x8(SP)
  19. print.go:274 0x48cf8c 488d442440 LEAQ 0x40(SP), AX
  20. print.go:274 0x48cf91 4889442410 MOVQ AX, 0x10(SP)
  21. print.go:274 0x48cf96 48c744241801000000 MOVQ $0x1, 0x18(SP)
  22. print.go:274 0x48cf9f 48c744242001000000 MOVQ $0x1, 0x20(SP)
  23. print.go:274 0x48cfa8 e85397ffff CALL fmt.Fprintln(SB)
  24. print.go:274 0x48cfad 488b6c2450 MOVQ 0x50(SP), BP
  25. print.go:274 0x48cfb2 4883c458 ADDQ $0x58, SP
  26. print.go:274 0x48cfb6 c3 RET
  27. main.go:18 0x48cfb7 e8e447fcff CALL runtime.morestack_noctxt(SB)
  28. main.go:18 0x48cfbc e96fffffff JMP main.main(SB)
qq24tv8q

qq24tv8q1#

Golang规范是这样描述defer语句的:
[...]如果延迟函数是一个函数字面量**,并且周围的函数具有在字面量范围内的命名结果参数**,则延迟函数可以在返回结果参数之前访问和修改结果参数。
(my强调)
your first snippet中,函数test没有命名的返回参数; x只是一个局部变量。因此,defer语句不能修改函数test的结果。
your second snippet中,函数test有一个命名的返回参数x,它在函数字面量的范围内。因此,defer语句可以(并且确实)修改函数test的结果。

相关问题