深入理解C语言的函数调用过程
我们先来看一段简单的C语言代码:
#include <stdio.h> int Add(int x, int y) { int z = 0; z = x + y; return z; } int main() { int a = 10; int b = 20; int ret = Add(a, b); printf("ret = %d\n", ret); return 0; }
我们发现其实main函数在 __tmainCRTStartup 函数中调⽤用的,⽽而 __tmainCRTStartup 函数是 在 mainCRTStartup 被调⽤用的。
我们知道每⼀一次函数调⽤用都是⼀一个过程。 这个过程我们通⻓长称之为:函数的调⽤用过程。 这个过程要为函数开辟栈空间,⽤用于本次函数的调⽤用中临时变量量的保存、现场保护。这块栈空间 我们称之为函数栈帧。
⽽而栈帧的维护我们必须了了解ebp和esp两个寄存器器。
ebp:存放栈底的寄存器
esp:存放栈顶的寄存器
函数的临时变量都是在函数的栈帧结构上定义的
在函数调⽤用的过程中这两个寄存器器存放了维护这个栈的栈底和栈顶指针。
比如: 调⽤用main函数,我们为main函数分配栈帧空间,那么栈帧维护如下:
函数被调用时:
1) EIP/EBP成为新函数栈的边界
函数被调用时,返回时的EIP首先被压入堆栈;创建栈框架时,上级函数栈的EBP被压入堆栈,与EIP一道行成新函数栈框架的边界。
2) EBP成为栈帧指针STP,用来指示新函数栈的边界
栈帧建立后,EBP指向的栈的内容就是上一级函数栈的EBP,可以想象,通过EBP就可以把层层调用函数的栈都回朔遍历一遍,调试器就是利用这个特性实现backtrace功能的。
3) ESP总是作为栈指针指向栈顶,用来分配栈空间
栈分配空间给函数局部变量时的语句通常就是给ESP减去一个常数值,例如,分配一个整型数据就是 ESP-4。
4) 函数的参数传递和局部变量访问可以通过STP即EBP来实现
由于栈框架指针永远指向当前函数的栈基地址,参数和局部变量访问通常为如下形式:
+8+xx(%ebp) :函数入口参数的的访问
-xx(%ebp) :函数局部变量访问
假如函数A调用函数B,函数B调用函数C ,则函数栈帧及调用关系如下图所示:
+----------------------+----> 高地址 | EIP (上级函数返回地址) | +----------------------+ +--> | EBP (上级函数的EBP) | --+ <------ 当前函数A的EBP (即STP框架指针) | +----------------------+ +-->偏移量A | | Local Variables | | | | .......... | --+ <------ ESP指向函数A新分配的局部变量,局部变量可以通过A的ebp-偏移量A访问 | f +----------------------+ | r | Arg n(函数B的第n个参数) | | a +----------------------+ | m | Arg .(函数B的第.个参数) | | e +----------------------+ | | Arg 1(函数B的第1个参数) | | o +----------------------+ | f | Arg 0(函数B的第0个参数) | --+ <------ B函数的参数可以由B的ebp+偏移量B访问 | +----------------------+ +--> 偏移量B | A | EIP (A函数的返回地址) | | | +----------------------+ --+ +--- | EBP (A函数的EBP) |<--+ <------ 当前函数B的EBP (即STP框架指针) +----------------------+ | | Local Variables | | | .......... | | <------ ESP指向函数B新分配的局部变量 +----------------------+ | | Arg n(函数C的第n个参数) | | +----------------------+ | | Arg .(函数C的第.个参数) | | +----------------------+ +--> frame of B | Arg 1(函数C的第1个参数) | | +----------------------+ | | Arg 0(函数C的第0个参数) | | +----------------------+ | | EIP (B函数的返回地址) | | +----------------------+ | +--> | EBP (B函数的EBP) |---+ <------ 当前函数C的EBP (即STP框架指针) | +----------------------+ | | Local Variables | | | .......... | <------ ESP指向函数C新分配的局部变量 | +----------------------+----> 低地址 frame of C