一、在讲函数的堆栈调用之前,让我们先了解两个概念:
1.什么是栈以及栈的特点?
栈被定义为一个特殊的容器,用户可以将数据压入栈中,也可以将已经压栈的数据进行出栈;压栈操作使得栈增大,出栈使得栈减小;栈总是自下增长的,由高地址向低地址进行增长,栈顶指针由称为esp的寄存器进行定位,而栈底指针由称为ebp的寄存器进行定位(栈底指针不进行移动),压栈操作使得栈顶指针的地址减小,出栈的操作使栈顶地址会增大;
2.栈的作用
栈其实就是保存了一个函数调用所需要的维护信息,这常常被称为栈帧;栈帧一般包括:
函数的返回地址和参数;临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量;保存的上下文:包括在函数调用前后需要保持不变的寄存器;说了这么多,想必大家也理解了什么是栈了,接下来讲一下函数的堆栈调用:
函数调用的另一个词语表示叫作 过程。一个过程调用包括将数据和控制从代码的一部分传递到另一部分。
另外,它还必须在进入时为过程的局部变量分配空间,并在退出时释放这些空间。
而大多数机器的数据传递、局部变量的分配和释放通过操纵程序栈来实现。为单个过程(函数调用)分配的那部分栈称为栈帧(上面已经提到)。
例:假设过程P(调用者)调用过程Q(被调用者),则Q的参数放在P的栈帧中。另外,当P调用Q时,P中的返回地址被压入栈中,形成P的栈帧的末尾 (返回地址就是当程序从Q返回时应该继续执行的地方)。Q的栈帧从保存的帧指针的值开始,后面到新的栈指针之间就是该过程的部分了。
1. 在Main(调用方)函数中: 入栈:
压入调用main函数的函数maincrtstartup ()函数的栈底指针;开辟main函数的栈帧空间;压入形参以及调用函数;压入回退到调用方时,被调用方的下一条指令的地址;清栈:
清理形参变量地址;
2. Sum(被调用方)函数中:
入栈:
压入调用sum函数的main函数的栈底指针;开辟sum函数栈帧空间并且初始化0xCCCCCCCC;压入实参;退栈:
清理sum函数的栈帧;回退栈底指针;返回调用方栈帧后下一条指令;以上就是函数堆栈调用的具体过程,下面我用一个简单的程序来让大家理解一下:
1.将main()函数的mainCRTstartup函数的栈底指针进行压栈并初始化main函数的栈帧空间;;
2.在sum函数的计算过程中,都是借助于寄存器eax完成的,根据汇编代码可以看到将b和a的值放入eax寄存器进行求和之后又放入tmp中。
3.最后进行回退清理Sum函数的栈帧空间,出栈并会退到主函数栈底。
以上就是我总结的函数的堆栈调用,如有问题请各位同仁指出。
