函数调用的过程
先导知识
栈帧(Stack Frame)和函数是一一对应的:每次函数被调用时,都会为该函数创建一个独立的栈帧,用于存储函数调用所需的信息的数据结构,包括函数的局部变量、参数、返回地址和其他与函数执行相关的信息。
正文
可以从三个方面来考虑函数调用的过程:
- 控制转移
- 内存管理
- 参数传递
1. 控制转移
第一步,将当前PC所指向的指令的下一条指令的地址(即返回地址
)压栈,存放在当前函数栈帧的顶部(如下方左图)。然后将程序计数器
(PC )的值修改为被调函数的起始地址。
(注:在x86架构中,程序计数器被称为指令指针寄存器 Instruction Pointer
,简称IP,在其他体系结构中,它可能被称为PC或PC寄存器)
2. 内存管理
分配:栈帧的创建
通常,我们用栈基址寄存器
(BP)来保存当前函数的栈帧的起始位置,栈指针寄存器
(SP)保存栈顶位置。
调用函数会产生新的栈帧,栈基址 BP
的值要发生改变,当然,原来的值old BP需要保存,即需要被压入被调函数的栈帧中。此时,栈指针SP
指向的值即为被保存的栈基址
的值。随后就可以将栈基址
的内容修改为栈指针SP
的值,如下图所示
绝大多数情况下,被调函数在运行过程中都需要栈空间,SP
就会向低地址方向延伸,这样,栈基址
和栈指针
就又会指向当前栈帧的栈底和栈顶。新的栈帧中存储了备份的寄存器的值和自己的局部变量等。
释放:栈帧的销毁
与分配对称,销毁栈帧时,先将SP
移至BP
,即回到上图左侧的情形,然后弹栈,即将SP
的内容写入BP
:BP = *SP, SP ++ ;
,这样,BP
回到了调用前的位置,SP
也回到了调用前所在的栈顶位置。此时,被调函数的栈帧已经被销毁(栈帧中的数据并没有抹除),但是函数返回还没有完成,需要将再次弹栈,将SP
的内容,也就是之前压栈的返回地址弹入PC
,这样,程序又回到了原来的位置继续执行,一个函数的调用就结束了。
3. 参数传递
英特尔X64
架构中,函数调用的前6个参数会存放在寄存器中,如果还有更多的参数,会压入调用者的栈帧,被调者要去调用者的栈帧中获取。X86
架构中,函数的参数全部会被压入栈中。返回值由于只有一个(如果有的话),会直接存储在寄存器中,调用者去寄存器中取即可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!