9.9Go之函数之函数底层实现

9.9Go之函数之函数底层实现

为什么函数是语言的一个核心元素

由基于堆栈的程序执行模型决定的

分析函数底层实现的两种方式

  • 语言编译器源码

  • 反汇编


需要有一定的汇编基础

本章会用到的一些汇编指令

Go语言编译器特点:

  • 产生的汇编代码是中间抽象态,不是对机器码的映射

  • 有些寄存器真实存在,有些是抽象的寄存器

Go中一些抽象的寄存器:

  1. SB(Static base pointer):静态基址寄存器。和全局符号一起表示全局变量的地址

  2. FR(Frame pointer):栈帧寄存器。 指向当前函数调用栈帧的栈底位置

  3. PC(Program counter):程序计数器。 存放下一条指令的执行地址 --->一般是CALL、RET等指令隐式操作

  4. SP(Stack pointer):栈顶寄存器。

    1. 在函数调用前由主调函数设置SP

    2. 负责对栈空间进行分配或回收

特点:

Go 汇编编译器对内嵌汇编程序自动做了调整,增加了保护现场,以及函数调用前的保持 PC 、SP 偏移地址重定位等逻辑,反汇编代码更能反映程序的真实执行逻辑

函数调用规约

采取模式:

  • caller-save模式:由调用者负责保护寄存器

  • 在主调函数调用被调函数的前后有一个保存现场和恢复现场的动作

Go中多返回值实现的分析

交换函数swap:

package main

import "fmt"

/*
手写一个交换函数,实现传入两个数,交换他们的值
*/
func swap(arr int, brr int) (int, int) { //函数调用前己经为返回值和参数分配了栈空间,分配顺序是从右向左的,先是返回值,然后是参数
/*交换值*/
arr, brr = brr, arr
return arr, brr
}

func main() {
fmt.Println(swap(10, 20))
}

省略掉汇编代码,只看结果可知:

  • 函数的调用者负责环境准备,包括为参数和返回值开辟栈空间。

  • 寄存器的保存和恢复也由调用方负责。

  • 函数调用后回收栈空间,恢复 BP 也由主调函数负责。

函数多指返回的实质:

  • 在栈上开辟多个地址分别存放返回值

  • 如果返回值是存放到堆上,则多了一个复制的动作

main调用swap的栈的结构图:

函数多值返回的实现:

  • 主调函数预先分配好空间来存放返回值,被调函数执行时将返回值复制到该返回位置

  • 分配空间的特点:

Go语言闭包实现的分析

闭包的结构:

  • 函数指针

  • 对外部环境的引用

Go闭包的示例代码:

package main

/*
实现一个闭包函数,打印形参的值
*/
func close(arr int) func() {
return func() {
print(arr)
}
}

func main() {
//设置函数变量
f := close(100)
//调用函数
f()
}

在汇编中是通过返回一个结构体来实现闭包的功能:

type Closure struct {
   F uintptr
   env *Type
}

代码分析:

  • F 是返回的匿名函数指针

  • env 是对外部环境变量的引用集合

    • 如果闭包内没有修改外部变量,则 Go 编译器直接优化为值传递。否则是通过指针传递

章节小结:

  • 函数的底层实现在汇编层面的应用

  • 多值返回在内存层面的实现

  • 闭包在汇编层面的实现以及闭包所返回的结构体的字段内容

原文地址:https://www.cnblogs.com/JunkingBoy/p/15248495.html