一个被函数在栈上的情况:(栈从高地址向低地址延伸)
- 返回地址(函数执行结束后,会跳转到这个地址执行)
- BP(函数的栈基)
- 局部变量
- 返回值(指的是函数返回值,eg: a := function() ,这里的a就是返回值)(golang现在将返回值存到寄存器?待确认)
- 参数
- SP(函数栈指针)
函数栈帧会一次性分配,然后通过sp + 偏移量的方式来定位位置,当一个函数内调用多个函数时,会根据最大的函数栈帧来给每个函数分配栈帧。
call指令
在调用函数时,编译器会执行call指令,call指令主要有两步操作:
- 将下一条指令的地址入栈 (这里的下一条指令地址也就是返回地址)
- 将ip寄存器指向代码段的被调用函数处
ret指令
当函数执行完成后,编译器会执行ret指令:
- 将返回地址弹出
- 跳转到返回地址
一个函数被调用的完整顺序
call指令 ——> sp指针向下移动,分配足够大栈帧空间 ——> 将调用者栈基bp复制入栈(为区分记为bp1)——>将bp1存到寄存器——>执行函数——>将寄存器的值恢复为bp——>释放栈帧——> ret指令
return与defer的执行顺序:
- return给返回值赋值
- defer函数执行
- return返回返回值
详情见幼麟实验室