函数是任何编程语言不可缺少的语法机制,函数通常包括函数声明、定义、调用。调用及多层嵌套调用时会有一个回溯的问题,也就是回到原来函数调用的位置,如何实现呢,使用栈的后进先出机制。类似于你去一个地方,经过了n个叉路口,你在每个叉路口用一个信封标识你选择的路线,叠在一起,返回时,依次从上面取出信封,按信封标识的信息即可回到起始点。
我们知道,计算机识别的是0、1串的机器语言表示的CPU指令集,0、1串抽象为字符版就是汇编指令及其语法机制,由汇编器翻译。汇编指令的进一步抽象就是高级语言指令及语法机制,由编译器或解释器翻译。
了解高级语言的函数调用,对应的汇编指令、栈空间及操作,可以更好地理解程序的运行机制。
我们知道,数据及指令的存储在计算机系统概念中是一个分层的体系结构,由内到外依次是C寄存器(晶体管实现的循环电路)、CPU缓存(SRAM,晶体管实现的双稳态电路)、内存(DRAM,电容存储,需周期刷新充电)、硬盘缓存、硬盘(机械、磁存储)等外部存储,各层离CPU由近到远,速度由快到慢。
一、常用寄存器
EAX(accumulator):通用寄存器。相对其他寄存器,在进行运算方面比较常用。在保护模式中,也可以作为内存偏移指针(此时,DS作为段寄存器或选择器)
EBX(base):通用寄存器。通常作为内存偏移指针使用(相对于EAX、ECX、EDX),DS是默认的段寄存器或选择器。在保护模式中,同样可以起这个作用。
ECX(count):通用寄存器。通常用于特定指令的计数。在保护模式中,也可以作为内存偏移指针(此时,DS作为寄存器或段选择器)。
EDX(data):通用寄存器。在某些运算中作为EAX的溢出寄存器(例如乘、除)。
SI(Source Index):源变址寄存器 ,通常在内存操作指令中作为“源地址指针”使用。当然,ESI可以被装入任意的数值,但通常没有人把它当作通用寄存器来用。
EDI(Destination Index):通常在内存操作指令中作为“目的地址指针”使用。当然,EDI也可以被装入任意的数值,但通常没有人把它当作通用寄存器来用。ES是默认段寄存器或选择器。
EBP(Base Pointer)和ESP(Stack Pointer):作为指针的寄存器,也可作为16位寄存器BP, SP使用,常用于椎栈操作。通常,它被高级语言编译器用以建造‘堆栈帧'来保存函数或过程的局部变量。
EIP是一个32位宽的寄存器,同CS一同指向即将执行的那条指令的地址,存放指令的偏移地址。微处理器工作于实模式下,EIP是IP(16位,Instruction Pointer,指令指针寄存器 )寄存器。不能够直接修改这个寄存器的值,修改它的方法是跳转或分支指令。(CS(Code Segment)代码段寄存器 )
二、汇编语言命令
1、mov——传送指令
定义:把一个字节、字或双字的操作数从源位置传送到目的位置,可以实现立即数到通用寄存器或主存的传送,通用寄存器与通用寄存器、主存或段寄存器之间的传送,主存与段寄存器之间的传送。
举例:mv ebp, esp
解释:相当于C语言中赋值语句=,ebp=esp
2、push——进栈指令
定义:进栈指令push先将ESP减小作为当前栈顶,然后可以将立即数、通用寄存器和段寄存器或存储器操作数传送到当前栈顶。
格式:push src
举例:push ebp
解释:相当于C语言中esp+=4, *esp=ebp
作用:为ebp当前存放的地址,在栈顶开辟空间存入它,用作调用子函数时的现场保护
3、pop——出栈指令
定义:与入栈指令相反,它先将栈顶的数据传送到通用寄存器、存储单元或段寄存器中,然后ESP增加作为当前栈顶。
格式:pop src
举例:pop ebp
解释:相当于C语言中 ebp=*esp, esp+=4
作用:调用子函数结束后,恢复主函数的ebp
4、add——加法指令
格式:add dest, src
解释:相当于dest+=src
5、sub——减法指令
格式:sub dest, src
解释:相当于dest-=src
6、call——函数调用指令
格式:call 函数名
作用:(1)将程序当前执行的位置IP压入堆栈中;(2)转移到调用的子程序。
三、函数调用实例
#include using namespace std;int d=11;int add(int a, int b) {int c=a+b; return c;}int main() { int x=10; int y=20; int z=add(x,y)+d; return 0; }
四、函数调用实例与汇编代码
1: #include 2: using namespace std;3: int d=11;4: int add(int a, int b)5: {00401250 push ebp//栈底指针寄存器ebp入栈00401251 mov ebp,esp// ebp=esp(栈(顶)指针寄存器)00401253 sub esp,44h// esp-=44h00401256 push ebx// "基地址"(base)寄存器00401257 push esi//源通用寄存器00401258 push edi//目的通用寄存器00401259 lea edi,[ebp-44h]//把ebp-44h加载到edi中,目的是保存局部变量的区域0040125C mov ecx,11h 00401261 mov eax,0CCCCCCCCh //0xCCCCCCCC代表着未被初始化00401266 rep stos dword ptr [edi]//rep是重复其上面的指令,ECX是重复的次数//STOS指令将eax中的值拷贝到ES:EDI指向的地址6: int c=a+b;00401268 mov eax,dword ptr [ebp+8] //是指将ebp偏移8个字节的指针对应的4个字节赋给eax0040126B add eax,dword ptr [ebp+0Ch]0040126E mov dword ptr [ebp-4],eax7: return c;00401271 mov eax,dword ptr [ebp-4]8: }00401274 pop edi00401275 pop esi00401276 pop ebx00401277 mov esp,ebp00401279 pop ebp0040127A ret--- No source file ------------------------------------------------------------------------------0040127B int 3……--- F:WebsiteotesVC语法estest.cpp -------------------------------------------------------9:10: int main()11: {00401290 push ebp00401291 mov ebp,esp00401293 sub esp,4Ch00401296 push ebx00401297 push esi00401298 push edi00401299 lea edi,[ebp-4Ch]0040129C mov ecx,13h004012A1 mov eax,0CCCCCCCCh004012A6 rep stos dword ptr [edi]12: int x=10;004012A8 mov dword ptr [ebp-4],0Ah13: int y=20;004012AF mov dword ptr [ebp-8],14h14: int z=add(x,y)+d;004012B6 mov eax,dword ptr [ebp-8]004012B9 push eax004012BA mov ecx,dword ptr [ebp-4]004012BD push ecx004012BE call @ILT+0(add) (00401005)004012C3 add esp,8004012C6 add eax,dword ptr [d (00434dc0)]004012CC mov dword ptr [ebp-0Ch],eax15: return 0;004012CF xor eax,eax16: }004012D1 pop edi004012D2 pop esi004012D3 pop ebx004012D4 add esp,4Ch004012D7 cmp ebp,esp004012D9 call __chkesp (004081b0)004012DE mov esp,ebp004012E0 pop ebp004012E1 ret
-End-