本文已参与「新人创作礼」活动,一起开启掘金创作之路。
这篇文章主要介绍过程调用具体在 CPU 和内存中是怎么实现的。理解之后,对于递归会有更加清晰的认识。
过程调用
- 传递控制:调用执行过程代码(call),以及返回调用函数的位置(ret)
- 传递数据:包括过程需要的参数以及过程的返回值
- 内存管理:如何在过程执行的时候分配内存,以及在返回之后释放内存
栈帧
当函数执行所需要的存储空间超出寄存器能够存放的大小时,
就会借助栈上的存储空间,我们把这部分存储空间称为函数的栈帧
运行时栈
所谓的栈,实际上一块内存区域,可以看做是一块连续的大数组
下图中箭头所指的就是寄存器 %rsp 的值,这个寄存器是栈指针 ,用来记录栈顶的位置。
- 往栈内添加数据,其地址是递减的
- 栈底存储的是最先放入栈中的数据,满足先进后出的原则。
- 因此栈顶的地址是最小的。
弹栈压栈
push操作(压栈):对应两个操作 -> %rsp地址-8,将操作数的值写入到栈中对应的位置
pop操作(弹栈):也对应两个操作 -> %rsp地址+8,将栈中位置的数据返回(对象是一个寄存器)
对于pop操作,其实只是移动了栈指针的位置,并不意味着内存上数据的"消除",这是那块数据不再是栈的一部分了
call指令与ret指令
指令call要做两件事:
- 将函数的第一条指令的地址写入到程序指令寄存器rip中,以此实现函数调用。
- 同时还要将返回地址压入栈中。
这个返回地址就是函数调用执行完毕后,下一条指令的地址。
- 当函数执行完毕,指令ret从栈中将返回地址弹出,写入到程序指令寄存器rip中。
- 函数返回,继续执行main函数中相关的操作。以上整个过程就是函数调用与返回所涉及的操作。