函数是一组执行特定任务的代码,可以在需要时通过调用它来使用。
在使用多个函数调用或递归时,为了更好地理解代码,非常有必要知道函数调用的概念。
现在,让我们了解函数调用的工作原理
在理解函数调用的工作之前,您需要一些有关CPU中的程序执行,程序堆栈,堆栈帧(激活记录)的先决知识。
- 程序堆栈:程序堆栈是保存所有函数调用的堆栈,底部元素为主要函数。
- 堆栈帧:堆栈帧实际上是一个缓冲存储器,它是程序堆栈的一个元素,并具有被调用函数的数据,即
- 退货地址
- 输入参数
- 本地Variabes
- 注册储蓄
- 堆栈指针:堆栈指针是指向程序堆栈顶部(即最近调用的函数)的指针。
现在,每当调用一个函数时,就会使用该函数的所有数据创建一个新的堆栈框架,并将该堆栈框架推入程序堆栈中,并且始终指向程序堆栈顶部的堆栈指针将按原样指向被推入的堆栈框架在程序堆栈的顶部。
调用函数时的一系列操作:
- 堆栈框架被推入堆栈。
- 执行子例程指令。
- 从堆栈弹出堆栈框架。
- 现在程序计数器正在保存返回地址。
注意:汇编语言的POP指令删除了堆栈的顶部,并将其分配给程序计数器。
让我们借助一个示例来理解:
在上述实例中调用一个函数。
- 程序计数器指向下一个指令存储位置,即执行函数之前的104,而100是调用函数的存储位置。
- 为了恢复返回地址,将程序计数器的内容压入堆栈。现在,存储在堆栈顶部的地址是104。
- 调用函数执行:现在程序计数器指向2000,这是子例程的起始地址,执行完子例程中的所有后续指令后,该地址将从堆栈中弹出。
- 弹出堆栈是指卸下堆栈的顶部并分配给程序计数器。现在,程序计数器正在保存返回地址,即104。
本文背后的主要动机是为了了解将返回地址压入堆栈的原因。然而,在函数的现代实现中,仅将返回地址推入堆栈是不够的。在推送返回地址之前,我们需要推送实际和形式参数。