专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

golang 函数function

1、函数概述

在Go语言中,function被视为一种类型,既然是一种类型,那么我们就可以把function类型当作普通的类型来操作。 函数特征:

  • 不支持默认参数
  • 不支持重载
  • 支持不定长参数, 不定长参数只能是参数列表中最后的一个,后面不能再出现其他的参数
  • 多返回值
  • 命名返回值参数
  • 匿名函数
  • 闭包。
package main

func main() {
    a := 1
    b := 1
    _ = sum(a, b)
}

func sum(a int, b int) (c int) {
    c =  a+ b
    return c
}

从汇编层面理解上面的函数逻辑go tool compile -S -N -l fundemo1.go(不优化,不内联):

    "".main STEXT size=87 args=0x0 locals=0x30
    0x0000 00000 (fundemo1.go:5)    TEXT    "".main(SB), ABIInternal, $48-0 // 函数栈空间为48字节,参数和返回值大小为0,栈不分裂
    0x0000 00000 (fundemo1.go:5)    MOVQ    (TLS), CX
    0x0009 00009 (fundemo1.go:5)    CMPQ    SP, 16(CX)   // 判断栈空间是否需要分配更多的栈
    0x000d 00013 (fundemo1.go:5)    JLS 80
    0x000f 00015 (fundemo1.go:5)    SUBQ    $48, SP    // 分配栈空间,48个字节
    0x0013 00019 (fundemo1.go:5)    MOVQ    BP, 40(SP) // 将基址指针存储到栈上(40)SP,一个字节
    0x0018 00024 (fundemo1.go:5)    LEAQ    40(SP), BP // 把 40(SP) 的地址放到 BP 里。
    0x001d 00029 (fundemo1.go:5)    FUNCDATA    $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x001d 00029 (fundemo1.go:5)    FUNCDATA    $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x001d 00029 (fundemo1.go:5)    FUNCDATA    $3, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x001d 00029 (fundemo1.go:6)    PCDATA  $2, $0
    0x001d 00029 (fundemo1.go:6)    PCDATA  $0, $0
    0x001d 00029 (fundemo1.go:6)    MOVQ    $1, "".a+32(SP)  // 定义局部变量a
    0x0026 00038 (fundemo1.go:7)    MOVQ    $1, "".b+24(SP)  // 定义局部变量b
    0x002f 00047 (fundemo1.go:8)    MOVQ    "".a+32(SP), AX
    0x0034 00052 (fundemo1.go:8)    MOVQ    AX, (SP)      // 将变量a的值放在sp开启的一个字节里面
    0x0038 00056 (fundemo1.go:8)    MOVQ    $1, 8(SP)     // 第二个参数放在第二个字节里面
    0x0041 00065 (fundemo1.go:8)    CALL    "".sum(SB)   // 调用sum函数
    0x0046 00070 (fundemo1.go:9)    MOVQ    40(SP), BP  // 来恢复栈基址指针
    0x004b 00075 (fundemo1.go:9)    ADDQ    $48, SP // 销毁已经失去作用的48字节空间。
    0x004f 00079 (fundemo1.go:9)    RET
    0x0050 00080 (fundemo1.go:9)    NOP
    0x0050 00080 (fundemo1.go:5)    PCDATA  $0, $-1
    0x0050 00080 (fundemo1.go:5)    PCDATA  $2, $-1
    0x0050 00080 (fundemo1.go:5)    CALL    runtime.morestack_noctxt(SB)
    0x0055 00085 (fundemo1.go:5)    JMP 0
    0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 76 41 48  eH..%....H;a.vAH
    0x0010 83 ec 30 48 89 6c 24 28 48 8d 6c 24 28 48 c7 44  ..0H.l$(H.l$(H.D
    0x0020 24 20 01 00 00 00 48 c7 44 24 18 01 00 00 00 48  $ ....H.D$.....H
    0x0030 8b 44 24 20 48 89 04 24 48 c7 44 24 08 01 00 00  .D$ H..$H.D$....
    0x0040 00 e8 00 00 00 00 48 8b 6c 24 28 48 83 c4 30 c3  ......H.l$(H..0.
    0x0050 e8 00 00 00 00 eb a9                             .......
    rel 5+4 t=16 TLS+0
    rel 66+4 t=8 "".sum+0
    rel 81+4 t=8 runtime.morestack_noctxt+0
"".sum STEXT nosplit size=25 args=0x18 locals=0x0
    0x0000 00000 (fundemo1.go:11)   TEXT    "".sum(SB), NOSPLIT|ABIInternal, $0-24 //参数大小为24字节
    0x0000 00000 (fundemo1.go:11)   FUNCDATA    $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x0000 00000 (fundemo1.go:11)   FUNCDATA    $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x0000 00000 (fundemo1.go:11)   FUNCDATA    $3, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x0000 00000 (fundemo1.go:11)   PCDATA  $2, $0
    0x0000 00000 (fundemo1.go:11)   PCDATA  $0, $0
    0x0000 00000 (fundemo1.go:11)   MOVQ    $0, "".c+24(SP)   // 定义变量c
    0x0009 00009 (fundemo1.go:12)   MOVQ    "".a+8(SP), AX    // 将a放在寄存器中
    0x000e 00014 (fundemo1.go:12)   ADDQ    "".b+16(SP), AX   // 执行加操作
    0x0013 00019 (fundemo1.go:12)   MOVQ    AX, "".c+24(SP)
    0x0018 00024 (fundemo1.go:13)   RET
    0x0000 48 c7 44 24 18 00 00 00 00 48 8b 44 24 08 48 03  H.D$.....H.D$.H.
    0x0010 44 24 10 48 89 44 24 18 c3                       D$.H.D$..

111_1.png

package main

//import "fmt"

func main() {
    a := A(10)
    //fmt.Println(a(2))
    _ = a(2)
}

func A(x int) func(y int) int {
    return func (y int) int {
        return x + y
    }
}

上面就是一个简单的闭包的例子,我们可以简单分析一下,调用A(10)会返回一个函数,就是A函数中的一个匿名函数,在这个匿名函数中,我们可以使用自由变量x的值,虽然x的声明并不在该函数的作用域范围内。

"".main STEXT size=82 args=0x0 locals=0x20
    0x0000 00000 (demo5.go:5)   TEXT    "".main(SB), ABIInternal, $32-0
    0x0000 00000 (demo5.go:5)   MOVQ    (TLS), CX
    0x0009 00009 (demo5.go:5)   CMPQ    SP, 16(CX)
    0x000d 00013 (demo5.go:5)   JLS 75
    0x000f 00015 (demo5.go:5)   SUBQ    $32, SP
    0x0013 00019 (demo5.go:5)   MOVQ    BP, 24(SP)
    0x0018 00024 (demo5.go:5)   LEAQ    24(SP), BP
    0x001d 00029 (demo5.go:5)   FUNCDATA    $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x001d 00029 (demo5.go:5)   FUNCDATA    $1, gclocals·2a5305abe05176240e61b8620e19a815(SB)
    0x001d 00029 (demo5.go:5)   FUNCDATA    $3, gclocals·ebb0e8ce1793da18f0378b883cb3e122(SB)
    0x001d 00029 (demo5.go:6)   PCDATA  $2, $0
    0x001d 00029 (demo5.go:6)   PCDATA  $0, $0
    0x001d 00029 (demo5.go:6)   MOVQ    $10, (SP)         // 调用A函数的参数
    0x0025 00037 (demo5.go:6)   CALL    "".A(SB)          // 调用A函数
    0x002a 00042 (demo5.go:6)   PCDATA  $2, $1
    0x002a 00042 (demo5.go:6)   MOVQ    8(SP), DX         // A函数的返回值
    0x002f 00047 (demo5.go:6)   MOVQ    DX, "".a+16(SP)   // 变量a的定义,值为A(10)
    0x0034 00052 (demo5.go:8)   MOVQ    $2, (SP)          // a(2)函数的参数
    0x003c 00060 (demo5.go:8)   MOVQ    (DX), AX
    0x003f 00063 (demo5.go:8)   PCDATA  $2, $0 
    0x003f 00063 (demo5.go:8)   CALL    AX               //调用a函数a(2)
    0x0041 00065 (demo5.go:9)   MOVQ    24(SP), BP
    0x0046 00070 (demo5.go:9)   ADDQ    $32, SP
    0x004a 00074 (demo5.go:9)   RET
    0x004b 00075 (demo5.go:9)   NOP
    0x004b 00075 (demo5.go:5)   PCDATA  $0, $-1
    0x004b 00075 (demo5.go:5)   PCDATA  $2, $-1
    0x004b 00075 (demo5.go:5)   CALL    runtime.morestack_noctxt(SB)
    0x0050 00080 (demo5.go:5)   JMP 0
    0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 76 3c 48  eH..%....H;a.v<H
    0x0010 83 ec 20 48 89 6c 24 18 48 8d 6c 24 18 48 c7 04  .. H.l$.H.l$.H..
    0x0020 24 0a 00 00 00 e8 00 00 00 00 48 8b 54 24 08 48  $.........H.T$.H
    0x0030 89 54 24 10 48 c7 04 24 02 00 00 00 48 8b 02 ff  .T$.H..$....H...
    0x0040 d0 48 8b 6c 24 18 48 83 c4 20 c3 e8 00 00 00 00  .H.l$.H.. ......
    0x0050 eb ae                                            ..
    rel 5+4 t=16 TLS+0
    rel 38+4 t=8 "".A+0
    rel 63+0 t=11 +0
    rel 76+4 t=8 runtime.morestack_noctxt+0
"".A STEXT size=117 args=0x10 locals=0x20
    0x0000 00000 (demo5.go:11)  TEXT    "".A(SB), ABIInternal, $32-16
    0x0000 00000 (demo5.go:11)  MOVQ    (TLS), CX
    0x0009 00009 (demo5.go:11)  CMPQ    SP, 16(CX)
    0x000d 00013 (demo5.go:11)  JLS 110
    0x000f 00015 (demo5.go:11)  SUBQ    $32, SP
    0x0013 00019 (demo5.go:11)  MOVQ    BP, 24(SP)
    0x0018 00024 (demo5.go:11)  LEAQ    24(SP), BP
    0x001d 00029 (demo5.go:11)  FUNCDATA    $0, gclocals·ffd148479e14c29ee3c68361945c5d25(SB)
    0x001d 00029 (demo5.go:11)  FUNCDATA    $1, gclocals·663f8c6bfa83aa777198789ce63d9ab4(SB)
    0x001d 00029 (demo5.go:11)  FUNCDATA    $3, gclocals·9fb7f0986f647f17cb53dda1484e0f7a(SB)
    0x001d 00029 (demo5.go:11)  PCDATA  $2, $0
    0x001d 00029 (demo5.go:11)  PCDATA  $0, $0
    0x001d 00029 (demo5.go:11)  MOVQ    $0, "".~r1+48(SP)       // 定义返回值变量
    0x0026 00038 (demo5.go:12)  PCDATA  $2, $1
    0x0026 00038 (demo5.go:12)  LEAQ    type.noalg.struct { F uintptr; "".x int }(SB), AX
    0x002d 00045 (demo5.go:12)  PCDATA  $2, $0
    0x002d 00045 (demo5.go:12)  MOVQ    AX, (SP)
    0x0031 00049 (demo5.go:12)  CALL    runtime.newobject(SB)    // 创建一个结构体对象
    0x0036 00054 (demo5.go:12)  PCDATA  $2, $1
    0x0036 00054 (demo5.go:12)  MOVQ    8(SP), AX                // 上面的函数返回值放到AX
    0x003b 00059 (demo5.go:12)  PCDATA  $0, $1
    0x003b 00059 (demo5.go:12)  MOVQ    AX, ""..autotmp_3+16(SP)
    0x0040 00064 (demo5.go:12)  LEAQ    "".A.func1(SB), CX       // 函数地址放到CX
    0x0047 00071 (demo5.go:12)  PCDATA  $2, $0
    0x0047 00071 (demo5.go:12)  MOVQ    CX, (AX)                // 将匿名函数地址放入(AX)指向的地址,为F赋值
    0x004a 00074 (demo5.go:12)  PCDATA  $2, $1
    0x004a 00074 (demo5.go:12)  MOVQ    ""..autotmp_3+16(SP), AX   // AX现在里面放的是struct地址
    0x004f 00079 (demo5.go:12)  TESTB   AL, (AX)
    0x0051 00081 (demo5.go:12)  MOVQ    "".x+40(SP), CX
    0x0056 00086 (demo5.go:12)  PCDATA  $2, $0
    0x0056 00086 (demo5.go:12)  MOVQ    CX, 8(AX)              // 将x值赋值给struct的x
    0x005a 00090 (demo5.go:12)  PCDATA  $2, $1
    0x005a 00090 (demo5.go:12)  PCDATA  $0, $0
    0x005a 00090 (demo5.go:12)  MOVQ    ""..autotmp_3+16(SP), AX
    0x005f 00095 (demo5.go:12)  PCDATA  $2, $0
    0x005f 00095 (demo5.go:12)  PCDATA  $0, $2
    0x005f 00095 (demo5.go:12)  MOVQ    AX, "".~r1+48(SP) // 返回返回值变量,
    0x0064 00100 (demo5.go:12)  MOVQ    24(SP), BP
    0x0069 00105 (demo5.go:12)  ADDQ    $32, SP
    0x006d 00109 (demo5.go:12)  RET
    0x006e 00110 (demo5.go:12)  NOP
    0x006e 00110 (demo5.go:11)  PCDATA  $0, $-1
    0x006e 00110 (demo5.go:11)  PCDATA  $2, $-1
    0x006e 00110 (demo5.go:11)  CALL    runtime.morestack_noctxt(SB)
    0x0073 00115 (demo5.go:11)  JMP 0
    0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 76 5f 48  eH..%....H;a.v_H
    0x0010 83 ec 20 48 89 6c 24 18 48 8d 6c 24 18 48 c7 44  .. H.l$.H.l$.H.D
    0x0020 24 30 00 00 00 00 48 8d 05 00 00 00 00 48 89 04  $0....H......H..
    0x0030 24 e8 00 00 00 00 48 8b 44 24 08 48 89 44 24 10  $.....H.D$.H.D$.
    0x0040 48 8d 0d 00 00 00 00 48 89 08 48 8b 44 24 10 84  H......H..H.D$..
    0x0050 00 48 8b 4c 24 28 48 89 48 08 48 8b 44 24 10 48  .H.L$(H.H.H.D$.H
    0x0060 89 44 24 30 48 8b 6c 24 18 48 83 c4 20 c3 e8 00  .D$0H.l$.H.. ...
    0x0070 00 00 00 eb 8b                                   .....
    rel 5+4 t=16 TLS+0
    rel 41+4 t=15 type.noalg.struct { F uintptr; "".x int }+0
    rel 50+4 t=8 runtime.newobject+0
    rel 67+4 t=15 "".A.func1+0
    rel 111+4 t=8 runtime.morestack_noctxt+0
"".A.func1 STEXT nosplit size=55 args=0x10 locals=0x10
    0x0000 00000 (demo5.go:12)  TEXT    "".A.func1(SB), NOSPLIT|NEEDCTXT|ABIInternal, $16-16
    0x0000 00000 (demo5.go:12)  SUBQ    $16, SP
    0x0004 00004 (demo5.go:12)  MOVQ    BP, 8(SP)
    0x0009 00009 (demo5.go:12)  LEAQ    8(SP), BP
    0x000e 00014 (demo5.go:12)  FUNCDATA    $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x000e 00014 (demo5.go:12)  FUNCDATA    $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x000e 00014 (demo5.go:12)  FUNCDATA    $3, gclocals·ebb0e8ce1793da18f0378b883cb3e122(SB)
    0x000e 00014 (demo5.go:12)  PCDATA  $2, $0
    0x000e 00014 (demo5.go:12)  PCDATA  $0, $0
    0x000e 00014 (demo5.go:12)  MOVQ    8(DX), AX          // //DX是闭包对象的地址,(取内容),偏移量8上放的刚好是上下文的int
    0x0012 00018 (demo5.go:12)  MOVQ    AX, "".x(SP)       // 此时将上下文的int放入x中
    0x0016 00022 (demo5.go:12)  MOVQ    $0, "".~r1+32(SP)  // 定义返回值
    0x001f 00031 (demo5.go:13)  MOVQ    "".x(SP), AX       // 将x值放入AX
    0x0023 00035 (demo5.go:13)  ADDQ    "".y+24(SP), AX    // 执行加法运算,并放入AX
    0x0028 00040 (demo5.go:13)  MOVQ    AX, "".~r1+32(SP)  // 返回结果
    0x002d 00045 (demo5.go:13)  MOVQ    8(SP), BP
    0x0032 00050 (demo5.go:13)  ADDQ    $16, SP
    0x0036 00054 (demo5.go:13)  RET
    0x0000 48 83 ec 10 48 89 6c 24 08 48 8d 6c 24 08 48 8b  H...H.l$.H.l$.H.
    0x0010 42 08 48 89 04 24 48 c7 44 24 20 00 00 00 00 48  B.H..$H.D$ ....H
    0x0020 8b 04 24 48 03 44 24 18 48 89 44 24 20 48 8b 6c  ..$H.D$.H.D$ H.l
    0x0030 24 08 48 83 c4 10 c3                             $.H....

可以看到,闭包通过

type.noalg.struct {
    F uintptr
    "".x int 
} 

实现了持有上下文的变量x的值。

作为一种特殊的函数:方法函数,是在某个类型上,定义的函数。其实他们和全局函数是一样的,只不过函数名被修饰为特定的名称,例如下面的方法函数和全局函数

type A struct {
    Name string
}

func (a *A) print() {
    a.Name = "FuncA"
    fmt.Println("function A")
}

func print() {
    fmt.Println("function A")
}

接受者是指针类型

TEXT    "".(*A).print(SB), ABIInternal, $88-8

如果是非指针类型的,则

TEXT    "".A.print(SB), ABIInternal, $88-16

全局函数:

TEXT    "".print(SB), ABIInternal, $88-0

2、defer函数

该函数在函数体执行完成后,以逆顺序逐个执行,即便程序发生严重错误时也会执行,支持匿名函数调用,常用于资源清理,文件关闭。GO没有异常机制,但有panic/recover模式来处理错误。panic 可以在任何地方引发,但recover只有在defer调用但函数中有效。 注:defer关键词后面必须跟函数调用,而不是仅仅给出函数原型

package main

import "fmt"

func main() {
    var a, b int = 8 ,0
    fmt.Println(divFunc(a, b))
    for i:=0;i<3;i++ {
        defer func () {
            fmt.Println(i)
        }()
    } // 3 3 3
}

func divFunc (a, b int) int {
    defer func () {
        if err := recover(); err != nil{
            fmt.Println("panic in divFunc, the argument is a invalid")
        }
    }()
    return a / b
}

main函数中的for循环中的输出结果是3\n3\n3,稍微调整一下defer函数内容

for i:=0;i<3;i++ {
        defer func (a int) {
            fmt.Println(a)
        }(i)
    } // 2 1 0

go tool compile(不带参数的defer函数)看一下:

 "".main STEXT size=191 args=0x0 locals=0x30
    0x0000 00000 (demo1.go:5)   TEXT    "".main(SB), ABIInternal, $48-0
    0x0000 00000 (demo1.go:5)   MOVQ    (TLS), CX
    0x0009 00009 (demo1.go:5)   CMPQ    SP, 16(CX)
    0x000d 00013 (demo1.go:5)   JLS 181
    0x0013 00019 (demo1.go:5)   SUBQ    $48, SP
    0x0017 00023 (demo1.go:5)   MOVQ    BP, 40(SP)
    0x001c 00028 (demo1.go:5)   LEAQ    40(SP), BP
    0x0021 00033 (demo1.go:5)   FUNCDATA    $0, gclocals·69c1753bd5f81501d95132d08af04464(SB)
    0x0021 00033 (demo1.go:5)   FUNCDATA    $1, gclocals·568470801006e5c0dc3947ea998fe279(SB)
    0x0021 00033 (demo1.go:5)   FUNCDATA    $3, gclocals·6e8d7ea4abad763909b26991048ee1fe(SB)
    0x0021 00033 (demo1.go:8)   PCDATA  $2, $1
    0x0021 00033 (demo1.go:8)   PCDATA  $0, $0
    0x0021 00033 (demo1.go:8)   LEAQ    type.int(SB), AX
    0x0028 00040 (demo1.go:8)   PCDATA  $2, $0
    0x0028 00040 (demo1.go:8)   MOVQ    AX, (SP)
    0x002c 00044 (demo1.go:8)   CALL    runtime.newobject(SB)   // 创建一个int类型的变量
    0x0031 00049 (demo1.go:8)   PCDATA  $2, $1
    0x0031 00049 (demo1.go:8)   MOVQ    8(SP), AX               
    0x0036 00054 (demo1.go:8)   PCDATA  $0, $1
    0x0036 00054 (demo1.go:8)   MOVQ    AX, "".&i+32(SP)        // 该变量放在i里面32(SP)
    0x003b 00059 (demo1.go:8)   PCDATA  $2, $0
    0x003b 00059 (demo1.go:8)   MOVQ    $0, (AX)                // i = 0
    0x0042 00066 (demo1.go:8)   JMP 68
    0x0044 00068 (demo1.go:8)   PCDATA  $2, $1
    0x0044 00068 (demo1.go:8)   MOVQ    "".&i+32(SP), AX        // 将i值放到AX
    0x0049 00073 (demo1.go:8)   PCDATA  $2, $0
    0x0049 00073 (demo1.go:8)   CMPQ    (AX), $3               // i与3进行比较
    0x004d 00077 (demo1.go:8)   JLT 81                         //小于3跳转81
    0x004f 00079 (demo1.go:8)   JMP 165                        //大于等于3跳转165
    0x0051 00081 (demo1.go:9)   PCDATA  $2, $1
    0x0051 00081 (demo1.go:9)   MOVQ    "".&i+32(SP), AX         // 将i值放到AX
    0x0056 00086 (demo1.go:11)  MOVQ    AX, ""..autotmp_3+24(SP)
    0x005b 00091 (demo1.go:9)   MOVL    $8, (SP)                // 8放到(SP)处
    0x0062 00098 (demo1.go:9)   PCDATA  $2, $2
    0x0062 00098 (demo1.go:9)   LEAQ    "".main.func1·f(SB), CX
    0x0069 00105 (demo1.go:9)   PCDATA  $2, $1
    0x0069 00105 (demo1.go:9)   MOVQ    CX, 8(SP)              // defer函数放到8(SP)处
    0x006e 00110 (demo1.go:9)   PCDATA  $2, $0
    0x006e 00110 (demo1.go:9)   MOVQ    AX, 16(SP)             // i放到16(SP)处
    0x0073 00115 (demo1.go:9)   CALL    runtime.deferproc(SB)  //调用derferproc函数
    0x0078 00120 (demo1.go:9)   TESTL   AX, AX
    0x007a 00122 (demo1.go:9)   JNE 149                        // deferproc调用不正常
    0x007c 00124 (demo1.go:9)   JMP 126                        // 正常
    0x007e 00126 (demo1.go:8)   PCDATA  $2, $-2
    0x007e 00126 (demo1.go:8)   PCDATA  $0, $-2
    0x007e 00126 (demo1.go:8)   JMP 128
    0x0080 00128 (demo1.go:8)   PCDATA  $2, $1
    0x0080 00128 (demo1.go:8)   PCDATA  $0, $1
    0x0080 00128 (demo1.go:8)   MOVQ    "".&i+32(SP), AX       // 将i值放到AX
    0x0085 00133 (demo1.go:8)   PCDATA  $2, $0
    0x0085 00133 (demo1.go:8)   MOVQ    (AX), AX
    0x0088 00136 (demo1.go:8)   PCDATA  $2, $3
    0x0088 00136 (demo1.go:8)   MOVQ    "".&i+32(SP), CX
    0x008d 00141 (demo1.go:8)   INCQ    AX                     // 自增i
    0x0090 00144 (demo1.go:8)   PCDATA  $2, $0
    0x0090 00144 (demo1.go:8)   MOVQ    AX, (CX)
    0x0093 00147 (demo1.go:8)   JMP 68
    0x0095 00149 (demo1.go:9)   PCDATA  $0, $0
    0x0095 00149 (demo1.go:9)   XCHGL   AX, AX
    0x0096 00150 (demo1.go:9)   CALL    runtime.deferreturn(SB)
    0x009b 00155 (demo1.go:9)   MOVQ    40(SP), BP
    0x00a0 00160 (demo1.go:9)   ADDQ    $48, SP
    0x00a4 00164 (demo1.go:9)   RET
    0x00a5 00165 (demo1.go:13)  XCHGL   AX, AX
    0x00a6 00166 (demo1.go:13)  CALL    runtime.deferreturn(SB)
    0x00ab 00171 (demo1.go:13)  MOVQ    40(SP), BP
    0x00b0 00176 (demo1.go:13)  ADDQ    $48, SP
    0x00b4 00180 (demo1.go:13)  RET
    0x00b5 00181 (demo1.go:13)  NOP
    0x00b5 00181 (demo1.go:5)   PCDATA  $0, $-1
    0x00b5 00181 (demo1.go:5)   PCDATA  $2, $-1
    0x00b5 00181 (demo1.go:5)   CALL    runtime.morestack_noctxt(SB)
    0x00ba 00186 (demo1.go:5)   JMP 0
    0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 0f 86 a2  eH..%....H;a....
    0x0010 00 00 00 48 83 ec 30 48 89 6c 24 28 48 8d 6c 24  ...H..0H.l$(H.l$
    0x0020 28 48 8d 05 00 00 00 00 48 89 04 24 e8 00 00 00  (H......H..$....
    0x0030 00 48 8b 44 24 08 48 89 44 24 20 48 c7 00 00 00  .H.D$.H.D$ H....
    0x0040 00 00 eb 00 48 8b 44 24 20 48 83 38 03 7c 02 eb  ....H.D$ H.8.|..
    0x0050 54 48 8b 44 24 20 48 89 44 24 18 c7 04 24 08 00  TH.D$ H.D$...$..
    0x0060 00 00 48 8d 0d 00 00 00 00 48 89 4c 24 08 48 89  ..H......H.L$.H.
    0x0070 44 24 10 e8 00 00 00 00 85 c0 75 19 eb 00 eb 00  D$........u.....
    0x0080 48 8b 44 24 20 48 8b 00 48 8b 4c 24 20 48 ff c0  H.D$ H..H.L$ H..
    0x0090 48 89 01 eb af 90 e8 00 00 00 00 48 8b 6c 24 28  H..........H.l$(
    0x00a0 48 83 c4 30 c3 90 e8 00 00 00 00 48 8b 6c 24 28  H..0.......H.l$(
    0x00b0 48 83 c4 30 c3 e8 00 00 00 00 e9 41 ff ff ff     H..0.......A...
    rel 5+4 t=16 TLS+0
    rel 36+4 t=15 type.int+0
    rel 45+4 t=8 runtime.newobject+0
    rel 101+4 t=15 "".main.func1·f+0
    rel 116+4 t=8 runtime.deferproc+0
    rel 151+4 t=8 runtime.deferreturn+0
    rel 167+4 t=8 runtime.deferreturn+0
    rel 182+4 t=8 runtime.morestack_noctxt+0
"".main.func1 STEXT size=184 args=0x8 locals=0x78
    0x0000 00000 (demo1.go:9)   TEXT    "".main.func1(SB), ABIInternal, $120-8
    0x0000 00000 (demo1.go:9)   MOVQ    (TLS), CX
    0x0009 00009 (demo1.go:9)   CMPQ    SP, 16(CX)
    0x000d 00013 (demo1.go:9)   JLS 174
    0x0013 00019 (demo1.go:9)   SUBQ    $120, SP
    0x0017 00023 (demo1.go:9)   MOVQ    BP, 112(SP)
    0x001c 00028 (demo1.go:9)   LEAQ    112(SP), BP
    0x0021 00033 (demo1.go:9)   FUNCDATA    $0, gclocals·533adcd55fa5ed3e2fd959716125aef9(SB)
    0x0021 00033 (demo1.go:9)   FUNCDATA    $1, gclocals·439b0b339525dcecc112fff85820bb4d(SB)
    0x0021 00033 (demo1.go:9)   FUNCDATA    $3, gclocals·f6aec3988379d2bd21c69c093370a150(SB)
    0x0021 00033 (demo1.go:9)   FUNCDATA    $4, "".main.func1.stkobj(SB)
    0x0021 00033 (demo1.go:10)  PCDATA  $2, $1
    0x0021 00033 (demo1.go:10)  PCDATA  $0, $1
    0x0021 00033 (demo1.go:10)  MOVQ    "".&i+128(SP), AX       // 取i值(i地址+128(SP)),放到AX
    0x0029 00041 (demo1.go:10)  PCDATA  $2, $0
    0x0029 00041 (demo1.go:10)  MOVQ    (AX), AX
    0x002c 00044 (demo1.go:10)  MOVQ    AX, ""..autotmp_2+48(SP)
    0x0031 00049 (demo1.go:10)  MOVQ    AX, (SP)
    0x0035 00053 (demo1.go:10)  CALL    runtime.convT64(SB)
    0x003a 00058 (demo1.go:10)  PCDATA  $2, $1
    0x003a 00058 (demo1.go:10)  MOVQ    8(SP), AX
    0x003f 00063 (demo1.go:10)  PCDATA  $2, $0
    0x003f 00063 (demo1.go:10)  PCDATA  $0, $2
    0x003f 00063 (demo1.go:10)  MOVQ    AX, ""..autotmp_3+64(SP)
    0x0044 00068 (demo1.go:10)  PCDATA  $0, $3
    0x0044 00068 (demo1.go:10)  XORPS   X0, X0
    0x0047 00071 (demo1.go:10)  MOVUPS  X0, ""..autotmp_1+72(SP)
    0x004c 00076 (demo1.go:10)  PCDATA  $2, $1
    0x004c 00076 (demo1.go:10)  PCDATA  $0, $2
    0x004c 00076 (demo1.go:10)  LEAQ    ""..autotmp_1+72(SP), AX
    0x0051 00081 (demo1.go:10)  MOVQ    AX, ""..autotmp_5+56(SP)
    0x0056 00086 (demo1.go:10)  TESTB   AL, (AX)
    0x0058 00088 (demo1.go:10)  PCDATA  $2, $2
    0x0058 00088 (demo1.go:10)  PCDATA  $0, $1
    0x0058 00088 (demo1.go:10)  MOVQ    ""..autotmp_3+64(SP), CX
    0x005d 00093 (demo1.go:10)  PCDATA  $2, $3
    0x005d 00093 (demo1.go:10)  LEAQ    type.int(SB), DX
    0x0064 00100 (demo1.go:10)  PCDATA  $2, $2
    0x0064 00100 (demo1.go:10)  MOVQ    DX, ""..autotmp_1+72(SP)
    0x0069 00105 (demo1.go:10)  PCDATA  $2, $1
    0x0069 00105 (demo1.go:10)  MOVQ    CX, ""..autotmp_1+80(SP)
    0x006e 00110 (demo1.go:10)  TESTB   AL, (AX)
    0x0070 00112 (demo1.go:10)  JMP 114
    0x0072 00114 (demo1.go:10)  MOVQ    AX, ""..autotmp_4+88(SP)
    0x0077 00119 (demo1.go:10)  MOVQ    $1, ""..autotmp_4+96(SP)
    0x0080 00128 (demo1.go:10)  MOVQ    $1, ""..autotmp_4+104(SP)
    0x0089 00137 (demo1.go:10)  PCDATA  $2, $0
    0x0089 00137 (demo1.go:10)  MOVQ    AX, (SP)
    0x008d 00141 (demo1.go:10)  MOVQ    $1, 8(SP)
    0x0096 00150 (demo1.go:10)  MOVQ    $1, 16(SP)
    0x009f 00159 (demo1.go:10)  CALL    fmt.Println(SB)
    0x00a4 00164 (demo1.go:11)  MOVQ    112(SP), BP
    0x00a9 00169 (demo1.go:11)  ADDQ    $120, SP
    0x00ad 00173 (demo1.go:11)  RET
    0x00ae 00174 (demo1.go:11)  NOP
    0x00ae 00174 (demo1.go:9)   PCDATA  $0, $-1
    0x00ae 00174 (demo1.go:9)   PCDATA  $2, $-1
    0x00ae 00174 (demo1.go:9)   CALL    runtime.morestack_noctxt(SB)
    0x00b3 00179 (demo1.go:9)   JMP 0
    0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 0f 86 9b  eH..%....H;a....
    0x0010 00 00 00 48 83 ec 78 48 89 6c 24 70 48 8d 6c 24  ...H..xH.l$pH.l$
    0x0020 70 48 8b 84 24 80 00 00 00 48 8b 00 48 89 44 24  pH..$....H..H.D$
    0x0030 30 48 89 04 24 e8 00 00 00 00 48 8b 44 24 08 48  0H..$.....H.D$.H
    0x0040 89 44 24 40 0f 57 c0 0f 11 44 24 48 48 8d 44 24  .D$@.W...D$HH.D$
    0x0050 48 48 89 44 24 38 84 00 48 8b 4c 24 40 48 8d 15  HH.D$8..H.L$@H..
    0x0060 00 00 00 00 48 89 54 24 48 48 89 4c 24 50 84 00  ....H.T$HH.L$P..
    0x0070 eb 00 48 89 44 24 58 48 c7 44 24 60 01 00 00 00  ..H.D$XH.D$`....
    0x0080 48 c7 44 24 68 01 00 00 00 48 89 04 24 48 c7 44  H.D$h....H..$H.D
    0x0090 24 08 01 00 00 00 48 c7 44 24 10 01 00 00 00 e8  $.....H.D$......
    0x00a0 00 00 00 00 48 8b 6c 24 70 48 83 c4 78 c3 e8 00  ....H.l$pH..x...
    0x00b0 00 00 00 e9 48 ff ff ff                          ....H...
    rel 5+4 t=16 TLS+0
    rel 54+4 t=8 runtime.convT64+0
    rel 96+4 t=15 type.int+0
    rel 160+4 t=8 fmt.Println+0
    rel 175+4 t=8 runtime.morestack_noctxt+0

defer函数主要涉及两个比较重要的函数:

  • runtime.deferproc
// 创建一个新的siz字节参数的defered函数fn,编辑器将一个defer语句转化为对该函数的调用
func deferproc(siz int32, fn *funcval) { // arguments of fn follow fn
    if getg().m.curg != getg() {
        // go code on the system stack can't defer
        throw("defer on system stack")
    }

    // the arguments of fn are in a perilous state. The stack map
    // for deferproc does not describe them. So we can't let garbage
    // collection or stack copying trigger until we've copied them out
    // to somewhere safe. The memmove below does that.
    // Until the copy completes, we can only call nosplit routines.
    sp := getcallersp()
    argp := uintptr(unsafe.Pointer(&fn)) + unsafe.Sizeof(fn)
    callerpc := getcallerpc()

    d := newdefer(siz)
    if d._panic != nil {
        throw("deferproc: d.panic != nil after newdefer")
    }
    d.fn = fn
    d.pc = callerpc
    d.sp = sp
    switch siz {
    case 0:
        // Do nothing.
    case sys.PtrSize:
        *(*uintptr)(deferArgs(d)) = *(*uintptr)(unsafe.Pointer(argp))
    default:
        memmove(deferArgs(d), unsafe.Pointer(argp), uintptr(siz))
    }

    // deferproc 正常情况下返回0
    // 恐慌的defer返回1
    // 如果deferproc返回!= 0,则编译器生成的代码将始终检查返回值并跳转到函数的末尾。
    return0()
    // 没有代码可以到这里-C返回寄存器已设置且不能破坏。
}

//将与延迟调用关联的参数存储在内存 _defer头之后。
//go:nosplit
func deferArgs(d *_defer) unsafe.Pointer {
    if d.siz == 0 {
        // Avoid pointer past the defer allocation.
        return nil
    }
    return add(unsafe.Pointer(d), unsafe.Sizeof(*d))
}

  • runtime.deferreturn
// 如果有,请运行一个延迟函数。 编译器在调用defer的任何函数的末尾插入对此的调用。
// 如果有被 defer 的函数的话,这里会调用 runtime·jmpdefer 跳到对应的位置
// 实际效果是会一遍遍地调用 deferreturn 直到 _defer 链表被清空
// 无法拆分堆栈,因为我们重用了调用方的框架来调用延迟的函数。

// 单个参数实际上并没有使用-它只是获取了地址,因此可以与待处理的延迟匹配。
//go:nosplit // 指示编译器不要在这个函数中插入检查栈是否溢出的代码。
func deferreturn(arg0 uintptr) {
    gp := getg()
    d := gp._defer
    if d == nil {
        return
    }
    sp := getcallersp()
    if d.sp != sp {
        return
    }

    // Moving arguments around.
    //
    // Everything called after this point must be recursively
    // nosplit because the garbage collector won't know the form
    // of the arguments until the jmpdefer can flip the PC over to
    // fn.
    switch d.siz {
    case 0:
        // Do nothing.
    case sys.PtrSize: //64位机上该值为8
        *(*uintptr)(unsafe.Pointer(&arg0)) = *(*uintptr)(deferArgs(d))
    default:
        memmove(unsafe.Pointer(&arg0), deferArgs(d), uintptr(d.siz))
    }
    fn := d.fn
    d.fn = nil
    gp._defer = d.link
    freedefer(d)
    jmpdefer(fn, uintptr(unsafe.Pointer(&arg0)))
}

TEST 指令设置零标志位, ZF, 当两个操作数进行And操作的结果值是0的时候,如果两个操作数相同,他们按位与结果为0,当他们都是0的时候。如果结果为0标识deferproc函数调用正常,跳转到0x1092ff9地址处开始执行,后面进行正常的自增变量,判断判断条件等等, 否则跳转到0x109302a继续执行,该指令后面就是处理deferreturn

   0x0000000001093024 <+100>:   test   %eax,%eax
   0x0000000001093026 <+102>:   jne    0x109302a <main.main+106>
   0x0000000001093028 <+104>:   jmp    0x1092ff9 <main.main+57>

自增i,当i大于3的时候,调到0x109303a继续执行。后面就是执行runtime.deferreturn 否则,

   0x0000000001092ffe <+62>:    incq   (%rax)
   0x0000000001093001 <+65>:    cmpq   $0x3,(%rax)
   0x0000000001093005 <+69>:    jge    0x109303a <main.main+122>

通过上面的分析,我们可看到,在没有传参的情况下defer函数,得到的是i的地址引用,当真正执行到defer函数时,此时的i值已经为3了,地址引用的值也是3.

到底是先执行return还是先执行defer函数,我们来看一下:

package main

import "fmt"

func main() {
     fmt.Println(deferReturn()) // 101
}

func deferReturn()  (ret int) {
      ret = 1
      defer func() {
           ret++
      }()
      return 100
}

go tool 看一下(为了方便看具体逻辑,去除了fmt.Prinltn调用)

"".main STEXT size=51 args=0x0 locals=0x10
    0x0000 00000 (demo4.go:7)   TEXT    "".main(SB), ABIInternal, $16-0
    0x0000 00000 (demo4.go:7)   MOVQ    (TLS), CX
    0x0009 00009 (demo4.go:7)   CMPQ    SP, 16(CX)
    0x000d 00013 (demo4.go:7)   JLS 44
    0x000f 00015 (demo4.go:7)   SUBQ    $16, SP
    0x0013 00019 (demo4.go:7)   MOVQ    BP, 8(SP)
    0x0018 00024 (demo4.go:7)   LEAQ    8(SP), BP
    0x001d 00029 (demo4.go:7)   FUNCDATA    $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x001d 00029 (demo4.go:7)   FUNCDATA    $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x001d 00029 (demo4.go:7)   FUNCDATA    $3, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x001d 00029 (demo4.go:10)  PCDATA  $2, $0
    0x001d 00029 (demo4.go:10)  PCDATA  $0, $0
    0x001d 00029 (demo4.go:10)  CALL    "".deferReturn(SB)   // deferReturn函数调用
    0x0022 00034 (demo4.go:11)  MOVQ    8(SP), BP
    0x0027 00039 (demo4.go:11)  ADDQ    $16, SP
    0x002b 00043 (demo4.go:11)  RET
    0x002c 00044 (demo4.go:11)  NOP
    0x002c 00044 (demo4.go:7)   PCDATA  $0, $-1
    0x002c 00044 (demo4.go:7)   PCDATA  $2, $-1
    0x002c 00044 (demo4.go:7)   CALL    runtime.morestack_noctxt(SB)
    0x0031 00049 (demo4.go:7)   JMP 0
    0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 76 1d 48  eH..%....H;a.v.H
    0x0010 83 ec 10 48 89 6c 24 08 48 8d 6c 24 08 e8 00 00  ...H.l$.H.l$....
    0x0020 00 00 48 8b 6c 24 08 48 83 c4 10 c3 e8 00 00 00  ..H.l$.H........
    0x0030 00 eb cd                                         ...
    rel 5+4 t=16 TLS+0
    rel 30+4 t=8 "".deferReturn+0
    rel 45+4 t=8 runtime.morestack_noctxt+0
"".deferReturn STEXT size=138 args=0x8 locals=0x20
    0x0000 00000 (demo4.go:13)  TEXT    "".deferReturn(SB), ABIInternal, $32-8
    0x0000 00000 (demo4.go:13)  MOVQ    (TLS), CX
    0x0009 00009 (demo4.go:13)  CMPQ    SP, 16(CX)
    0x000d 00013 (demo4.go:13)  JLS 128
    0x000f 00015 (demo4.go:13)  SUBQ    $32, SP
    0x0013 00019 (demo4.go:13)  MOVQ    BP, 24(SP)
    0x0018 00024 (demo4.go:13)  LEAQ    24(SP), BP
    0x001d 00029 (demo4.go:13)  FUNCDATA    $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x001d 00029 (demo4.go:13)  FUNCDATA    $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
    0x001d 00029 (demo4.go:13)  FUNCDATA    $3, gclocals·9fb7f0986f647f17cb53dda1484e0f7a(SB)
    0x001d 00029 (demo4.go:13)  PCDATA  $2, $0
    0x001d 00029 (demo4.go:13)  PCDATA  $0, $0
    0x001d 00029 (demo4.go:13)  MOVQ    $0, "".ret+40(SP)   // 定义返回值变量
    0x0026 00038 (demo4.go:14)  MOVQ    $1, "".ret+40(SP)   // 返回值变量赋值1
    0x002f 00047 (demo4.go:15)  MOVL    $8, (SP)            // SP第一个字节是常数8(1000)
    0x0036 00054 (demo4.go:15)  PCDATA  $2, $1
    0x0036 00054 (demo4.go:15)  LEAQ    "".deferReturn.func1·f(SB), AX    // f函数地址
    0x003d 00061 (demo4.go:15)  PCDATA  $2, $0
    0x003d 00061 (demo4.go:15)  MOVQ    AX, 8(SP)                         // SP第二个字节
    0x0042 00066 (demo4.go:15)  PCDATA  $2, $1
    0x0042 00066 (demo4.go:15)  LEAQ    "".ret+40(SP), AX
    0x0047 00071 (demo4.go:15)  PCDATA  $2, $0
    0x0047 00071 (demo4.go:15)  MOVQ    AX, 16(SP)                       // SP第三个自己,放返回值
    0x004c 00076 (demo4.go:15)  CALL    runtime.deferproc(SB)
    0x0051 00081 (demo4.go:15)  TESTL   AX, AX
    0x0053 00083 (demo4.go:15)  JNE 112                        // deferproc函数调用异常
    0x0055 00085 (demo4.go:15)  JMP 87                         // deferproc函数调用正常
    0x0057 00087 (demo4.go:18)  MOVQ    $100, "".ret+40(SP)    // 返回值赋值为100
    0x0060 00096 (demo4.go:18)  XCHGL   AX, AX
    0x0061 00097 (demo4.go:18)  CALL    runtime.deferreturn(SB)  
    0x0066 00102 (demo4.go:18)  MOVQ    24(SP), BP
    0x006b 00107 (demo4.go:18)  ADDQ    $32, SP
    0x006f 00111 (demo4.go:18)  RET
    0x0070 00112 (demo4.go:15)  XCHGL   AX, AX
    0x0071 00113 (demo4.go:15)  CALL    runtime.deferreturn(SB)
    0x0076 00118 (demo4.go:15)  MOVQ    24(SP), BP
    0x007b 00123 (demo4.go:15)  ADDQ    $32, SP
    0x007f 00127 (demo4.go:15)  RET
    0x0080 00128 (demo4.go:15)  NOP
    0x0080 00128 (demo4.go:13)  PCDATA  $0, $-1
    0x0080 00128 (demo4.go:13)  PCDATA  $2, $-1
    0x0080 00128 (demo4.go:13)  CALL    runtime.morestack_noctxt(SB)
    0x0085 00133 (demo4.go:13)  JMP 0
    0x0000 65 48 8b 0c 25 00 00 00 00 48 3b 61 10 76 71 48  eH..%....H;a.vqH
    0x0010 83 ec 20 48 89 6c 24 18 48 8d 6c 24 18 48 c7 44  .. H.l$.H.l$.H.D
    0x0020 24 28 00 00 00 00 48 c7 44 24 28 01 00 00 00 c7  $(....H.D$(.....
    0x0030 04 24 08 00 00 00 48 8d 05 00 00 00 00 48 89 44  .$....H......H.D
    0x0040 24 08 48 8d 44 24 28 48 89 44 24 10 e8 00 00 00  $.H.D$(H.D$.....
    0x0050 00 85 c0 75 1b eb 00 48 c7 44 24 28 64 00 00 00  ...u...H.D$(d...
    0x0060 90 e8 00 00 00 00 48 8b 6c 24 18 48 83 c4 20 c3  ......H.l$.H.. .
    0x0070 90 e8 00 00 00 00 48 8b 6c 24 18 48 83 c4 20 c3  ......H.l$.H.. .
    0x0080 e8 00 00 00 00 e9 76 ff ff ff                    ......v...
    rel 5+4 t=16 TLS+0
    rel 57+4 t=15 "".deferReturn.func1·f+0
    rel 77+4 t=8 runtime.deferproc+0
    rel 98+4 t=8 runtime.deferreturn+0
    rel 114+4 t=8 runtime.deferreturn+0
    rel 129+4 t=8 runtime.morestack_noctxt+0
"".deferReturn.func1 STEXT nosplit size=20 args=0x8 locals=0x0
    0x0000 00000 (demo4.go:15)  TEXT    "".deferReturn.func1(SB), NOSPLIT|ABIInternal, $0-8
    0x0000 00000 (demo4.go:15)  FUNCDATA    $0, gclocals·1a65e721a2ccc325b382662e7ffee780(SB)
    0x0000 00000 (demo4.go:15)  FUNCDATA    $1, gclocals·69c1753bd5f81501d95132d08af04464(SB)
    0x0000 00000 (demo4.go:15)  FUNCDATA    $3, gclocals·1cf923758aae2e428391d1783fe59973(SB)
    0x0000 00000 (demo4.go:16)  PCDATA  $2, $1
    0x0000 00000 (demo4.go:16)  PCDATA  $0, $0
    0x0000 00000 (demo4.go:16)  MOVQ    "".&ret+8(SP), AX
    0x0005 00005 (demo4.go:16)  PCDATA  $2, $0
    0x0005 00005 (demo4.go:16)  MOVQ    (AX), AX
    0x0008 00008 (demo4.go:16)  PCDATA  $2, $2
    0x0008 00008 (demo4.go:16)  PCDATA  $0, $1
    0x0008 00008 (demo4.go:16)  MOVQ    "".&ret+8(SP), CX
    0x000d 00013 (demo4.go:16)  INCQ    AX
    0x0010 00016 (demo4.go:16)  PCDATA  $2, $0
    0x0010 00016 (demo4.go:16)  MOVQ    AX, (CX)
    0x0013 00019 (demo4.go:17)  RET
    0x0000 48 8b 44 24 08 48 8b 00 48 8b 4c 24 08 48 ff c0  H.D$.H..H.L$.H..
    0x0010 48 89 01 c3                                      H...

所以,严格意义上说,defer函数是在调用defer函数的函数返回后执行的。

文章永久链接:https://tech.souyunku.com/41066

未经允许不得转载:搜云库技术团队 » golang 函数function

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们