程序的执行过程可看作连续的函数调用.当一个函数执行完毕时,程序要回到调用指令的下一条指令(紧接call指令)处继续执行.函数调用过程通常使用堆栈实现,每个用户态进程对应一个调用栈结构(call stack).编译器使用堆栈传递函数参数、保存返回地址、临时保存寄存器原有值(即函数调用的上下文)以备恢复以及存储本地局部变量.
不同处理器和编译器的堆栈布局、函数调用方法都可能不同,但堆栈的基本概念是一样的.
寄存器是处理器加工数据或运行程序的重要载体,用于存放程序执行中用到的数据和指令.所以呢函数调用栈的实现与处理器寄存器组密切相关.
函数调用栈的典型内存布局如下图所示:
其中,主调函数将参数按照调用约定依次入栈(图中为从右到左),然后将指令指针EIP入栈以保存主调函数的返回地址(下一条待执行指令的地址).进入被调函数时,被调函数将主调函数的帧基指针EBP入栈,并将主调函数的栈顶指针ESP值赋给被调函数的EBP(作为被调函数的栈底),接着改变ESP值来为函数局部变量预留空间.此时被调函数帧基指针指向被调函数的栈底.以该地址为基准,向上(栈底方向)可获取主调函数的返回地址、参数值,向下(栈顶方向)能获取被调函数的局部变量值,而该地址处又存放着上一层主调函数的帧基指针值.本级调用结束后,将EBP指针值赋给ESP,使ESP再次指向被调函数栈底以释放局部变量;再将已压栈的主调函数帧基指针弹出到EBP,并弹出返回地址到EIP.ESP继续上移越过参数,最终回到函数调用前的状态,即恢复原来主调函数的栈帧.如此递归便形成函数调用栈.
EBP指针在当前函数运行过程中(未调用其他函数时)保持不变.在函数调用前,ESP指针指向栈顶地址,也是栈底地址.在函数完成现场保护之类的初始化工作后,ESP会始终指向当前函数栈帧的栈顶,此时,若
调用函数时,实际上是从某一函数跳转到了被调用函数,这个时候,程序当前运行时所用的参数如果不进行保存的话,那么当执行完被调用函数跳转回原先的函数时,缺少必要的参数,程序就无法正常执行.所以呢,在调用函数之前,需要先保存现场信息,即先把参数压栈,然后再返回地址
在一个函数中调用另一个函数,会将当前状态入栈,等执行完下一个后出栈,恢复状态继续往下执行
例:
int
myfun()//假如指向到此函数
{
...
myfun1();
//调用myfun1函数,那我之前的变量怎么办?入栈保存,然后跳转到函数myfun1
....
}
myfun1()
return
0;
因为有些是不定参数的
这样函数内 在处理的时候 从左到右 会更方便.
根据前面的参数 来确定后面还有多少参数 这样.
要函数内部弹栈从左到右
那么压栈就只能从右到左了.
从右向左;
例如:f(int a, int b, int c)
c先入栈,然后b,其次a;
必须出栈!
aa 和 bb 和c 都是函数内部的局部变量,函数返回后就被释放,也就是在栈中没有了,返回后就剩下图中main()函数所对应的栈结构.
栈只能够通过出栈来减少栈中数据的个数,从反面来讲,如果不出栈,funcA()函数返回后,栈指针还是指向c那,这肯定是不对的,因为函数返回后栈指针就得指向man()的栈结构了.
以上就是土嘎嘎小编为大家整理的c语言被调用的函数先入栈相关主题介绍,如果您觉得小编更新的文章只要能对粉丝们有用,就是我们最大的鼓励和动力,不要忘记讲本站分享给您身边的朋友哦!!