深入分析golang多值返回以及闭包的实现

2020-01-28 12:05:40王旭

一、前言

golang有很多新颖的特性,不知道大家的使用的时候,有没想过,这些特性是如何实现的?当然你可能会说,不了解这些特性好像也不影响自己使用golang,你说的也有道理,但是,多了解底层的实现原理,对于在使用golang时的眼界是完全不一样的,就类似于看过http的实现之后,再来使用http框架,和未看过http框架时的眼界是不一样的,当然,你如果是一名it爱好者,求知欲自然会引导你去学习。

二、这篇文章主要就分析两点:

     1、golang多值返回的实现;

     2、golang闭包的实现;

三、golang多值返回的实现

我们在学C/C++时,很多人应该有了解过C/C++函数调用过程,参数是通过寄存器di和si(假设就两个参数)传递给被调用的函数,被调用函数的返回结果只能是通过eax寄存器返回给调用函数,因此C/C++函数只能返回一个值,那么我们是不是可以想象,golang的多值返回是否可以通过多个寄存器来实现的,正如用多个寄存器来传参一样?

这也是一种办法,但是golang并没有采用;我的理解是引入多个寄存器来存储返回值,会引起多个寄存器用途的重新约定,这无疑增加了复杂度;可以这么说,golang的ABI与C/C++非常不一样;

在从汇编角度分析golang多值返回之前,需要先熟悉golang汇编代码的一些约定, golang官网 有说明,这里重点说明四个symbols,需要注意的是这里的寄存器是伪寄存器:

       1.FP 栈底寄存器,指向一个函数栈的顶部;

       2.PC 程序计数器,指向下一条执行指令;

       3.SB 指向静态数据的基指针,全局符号;

       4.SP 栈顶寄存器;

这里面最重要的就是FP和SP,FP寄存器主要用于取参数以及存返回值,golang函数调用的实现很大程度上都是依赖这两个寄存器,这里先给出结果,


+-----------+---
| 返回值2 | 
+-----------+  
| 返回值1 |  
+---------+-+  
| 参数2 |  这些在调用函数中
+-----------+  
| 参数1 |   /
+-----------+  /
| 返回地址 | /
+-----------+--/-----fp值
| 局部变量 | 
| ... | 被调用数栈祯
|   | /
+-----------+--/+---sp值

这个就是golang的一个函数栈,也是说函数传参是通过fp+offset来实现的,而多个返回值也是通过fp+offset存储在调用函数的栈帧中。

下面通过一个例子来分析


package main

import "fmt"

func test(i, j int) (int, int) {
a:=i+ j
b:=i- j
 return a,b
}

func main() {
a,b:= test(2,1)
 fmt.Println(a, b)
}

这个例子很简单,主要是为了说明golang多值返回的过程;我们通过下面命令编译该程序