函数调用的过程

先导知识

栈帧(Stack Frame)和函数是一一对应的:每次函数被调用时,都会为该函数创建一个独立的栈帧,用于存储函数调用所需的信息的数据结构,包括函数的局部变量、参数、返回地址和其他与函数执行相关的信息。

正文

可以从三个方面来考虑函数调用的过程:

  1. 控制转移
  2. 内存管理
  3. 参数传递

1. 控制转移

第一步,将当前PC所指向的指令的下一条指令的地址(即返回地址)压栈,存放在当前函数栈帧的顶部(如下方左图)。然后将程序计数器(PC )的值修改为被调函数的起始地址。

(注:在x86架构中,程序计数器被称为指令指针寄存器 Instruction Pointer,简称IP,在其他体系结构中,它可能被称为PC或PC寄存器)

2. 内存管理

分配:栈帧的创建

通常,我们用栈基址寄存器(BP)来保存当前函数的栈帧的起始位置,栈指针寄存器(SP)保存栈顶位置。

调用函数会产生新的栈帧,栈基址 BP的值要发生改变,当然,原来的值old BP需要保存,即需要被压入被调函数的栈帧中。此时,栈指针SP指向的值即为被保存的栈基址的值。随后就可以将栈基址的内容修改为栈指针SP的值,如下图所示

保存栈基址

绝大多数情况下,被调函数在运行过程中都需要栈空间,SP就会向低地址方向延伸,这样,栈基址栈指针就又会指向当前栈帧的栈底和栈顶。新的栈帧中存储了备份的寄存器的值和自己的局部变量等。

栈帧扩大

释放:栈帧的销毁

与分配对称,销毁栈帧时,先将SP移至BP,即回到上图左侧的情形,然后弹栈,即将SP的内容写入BPBP = *SP, SP ++ ;,这样,BP回到了调用前的位置,SP也回到了调用前所在的栈顶位置。此时,被调函数的栈帧已经被销毁(栈帧中的数据并没有抹除),但是函数返回还没有完成,需要将再次弹栈,将SP的内容,也就是之前压栈的返回地址弹入PC,这样,程序又回到了原来的位置继续执行,一个函数的调用就结束了。

3. 参数传递

英特尔X64架构中,函数调用的前6个参数会存放在寄存器中,如果还有更多的参数,会压入调用者的栈帧,被调者要去调用者的栈帧中获取。X86架构中,函数的参数全部会被压入栈中。返回值由于只有一个(如果有的话),会直接存储在寄存器中,调用者去寄存器中取即可。

posted @   可奇  阅读(639)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示