golang 多返回值的实现原理-转载

之前一次面试时,面试官问到 你知道golang的多返回值的实现吗,一脸懵逼,平时主要注重项目应用开发,对这块确实没关注,答得不好,各位大佬,以后建议也加强下基础哦。

今天看看 golang 中多返回值的实现。

可以简单认为 c 中多返回值的实现,其实就是通过寄存器将返回参数以指针形式传入传入参数中,我们知道通过指针是保留一份数据的,实际的数据备份就只有一份,不管是否通过地址复制,实际内容就在那。

而 golang 不同。

栈帧

栈帧结构的两端由两个指针来指定。寄存器ebp通常用做帧指针(frame pointer),而esp则用作栈指针(stack pointer)。在函数执行过程中,栈指针esp会随着数据的入栈和出栈而移动,因此函数中对大部分数据的访问都基于帧指针ebp进行。

esp和ebp: esp是栈指针,是cpu机制决定的,push、pop指令会自动调整esp的值;ebp只是存取某时刻的esp,这个时刻就是进入一个函数内后,cpu会将esp的值赋给ebp,此时就可以通过ebp对栈进行操作,比如获取函数参数,局部变量等,实际上使用esp也可以;

  • EBP:基址指针寄存器(extended base pointer),存放一个指针,永远指向系统栈最上面一个栈帧的底部

  • ESP:栈指针寄存器(extended stack pointer),存放一个指针,永远指向系统栈最上面一个栈帧的栈顶

  • EAX 寄存器也叫做累加寄存器,用于存储函数的返回值外也用于执行计算的 操作。

调用函数中push ebp,将main函数的ebp压栈,然后mov ebp, esp将当前函数的esp赋给ebp,得到当前函数的栈底地址。

//函数调用之所以能够返回,单靠保持返回地址是不够的,这一步压栈动作很重要,
//因为我们要标记函数调用者栈帧的帧底,这样才能找出保存了的返回地址,
//栈顶是不用保存的,因为上一个栈帧的顶部会是func的栈帧底部。(两栈帧相邻的)
push ebp; 

//mov指令将esp寄存器的值赋值给ebp寄存器。上一栈帧的顶部,就是这个栈帧的底部
mov ebp, esp; 

调用函数结束之前,执行leave指令,其实该指令等于下面两条指令:此时fun相关数据全部被出栈,ebp将得重新到main函数的栈底地址,esp回到函数栈顶部.

mov esp, ebp //mov 将ebp的值 赋值给esp
pop ebp  //将堆栈中的ebp值弹出

go汇编中有4个伪寄存器

分别是:

  • FP: Frame pointer,指向栈底位置,一般用来引用函数的输入参数,用来访问函数的参数
  • PC: Program counter: 程序计数器,用于分支和跳转
  • SB: Static base pointer: 一般用于声明函数或者全局变量
  • SP: Stack pointer:指向当前栈帧的局部变量的开始位置(栈顶位置),一般用来引用函数的局部变量

在 C 语言中调用一个函数,函数的参数是通过寄存器和栈传递的,在 x86_64 的机器上,6 个以下(含 6 个)的参数会按照顺序分别使用 edi、esi、edx、ecx、r8d 和 r9d 六个寄存器传递,超过 6 个的剩余参数会通过栈进行传递;函数的返回值是通过 eax 寄存器进行传递的,这也就是为什么 C 语言中不支持多个返回值。

Golang 函数参数以及返回值都是通过SP来保存的,那么也就是说是通过堆栈寄存器来保存的。所以可以做到返回多个,因为它跟参数传递使用的都是堆栈。golang的返回参数是放在栈里面的,把返回值的地址存放到栈中,golang函数调用过程,是通过fp+offset来实现传参和返回值,而不像C/C++都是通过寄存器实现传参和返回值。 所以golang能够支持多个参数返回。

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

原文链接:https://blog.csdn.net/u014618114/article/details/119361688

posted on 2024-03-20 23:21  进击的davis  阅读(31)  评论(0编辑  收藏  举报

导航