函数调用包括函数代码间的“双向数据传递”与“执行控制转移”。“数据传递”通过函数参数和返回值来进行。另外,我们还需要在进入函数时为函数的局部变量分配存储空间,并且在退出函数时收回这部分空间,存储空间的分配与回收则通过栈操作来实现。“控制转移”CPU都提供相应的指令,如,CALL ,RET 等。
当调用以个函数时:
1)开辟该函数的栈空间(STACK FRAME);
2)将当前运行状态压栈;
3)将返回地址(函数调用的地方)压栈;
4)在栈内分配参数空间,传递参数信息;
4)执行被调用函数,如果有局部变量,则在栈内分配空间。(控制转交给被调函数)
假设函数A调用了函数B,栈内情况变化如下图所示:
当函数运行结束,退栈操作与函数调用相反;
1)释放内部局部变量的空间;
2)释放栈内参数空间;
3)退栈,得到返回地址,程序跳转到A处等待继续执行;
4)退栈,得到程序运行时候的状态,恢复调用函数前的状态;
5)释放该函数的栈空间。
备注:并不是所有的局部变量都保存在栈中,有时会保存在寄存器中,函数返回值也是一样。
Intel CPU 采用了所有函数必须遵守的寄存器用法统一惯例。该惯例指明,寄存器eax、edx和ecx的内容必须由调用者自己负责保存。当函数B被A调用时,函数B可以在不用保存这些寄存器内容的情况下任意使用它们而不会毁坏函数A所需要的任何数据。另外,寄存器ebx、esi和edi的内容则必须由被调用者B来保护。当被调用者需要使用这些寄存器中的任意一个时,必须首先在栈中保存其内容,并在退出时恢复这些寄存器的内容。因为调用者A(或者一些更高层的函数)并不负责保存这些寄存器内容,但可能在以后的操作中还需要用到原先的值。还有寄存器ebp和esp也必须遵守第二个惯例用法。
用递归模拟栈:
如果要实现“读取一个字符串,逆序输出”,即符合栈“先入后出”的特点。
函数调用-->栈,用递归函数调用模拟栈
Code Sample:
待更新。。。。