在参数传递中,有两个很重要的问题必须得到明确说明:
当参数个数多于一个时,按照什么顺序把参数压入堆栈函数调用后,由谁来把堆栈恢复原装。在高级语言中,通过函数调用约定来解决这两个问题。常见的调用约定有:
stdcall,cdecl,fastcall,thiscall,naked call
1)压栈顺序:函数参数从右到左
2)参数栈维护:由调用函数把参数弹出栈,传送参数的内存栈由调用函数来维护
(正因为如此,实现可变参数vararg的函数(如printf)只能使用该调用约定)
3)函数修饰名约定:VC将函数编译后会在函数名前面加上下划线前缀
4)每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大
(5) 由主程序执行“ADD ESP, n”指令调整 ESP,达到堆栈平衡。
2, __stdcall (Pascal方式清理C方式压栈,通常用于Win32 Api中)
1)压栈顺序:函数参数从右到左的压栈顺序
2)参数栈维护:被调用函数把参数弹出栈(在退出时清空堆栈)
3)函数修饰名约定:VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数
ex. VC: int f(void *p) (编译后)-> _f@4 (在外部汇编语言里可以用这个名字引用这个函数)
4)由子程序通过调用“RET n”指令主动平衡
1)压栈顺序:用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送
2)参数栈维护:被调用函数在返回前清理传送参数的内存栈
3)函数修饰名约定:VC将函数编译后会在函数名前面加上"@"前缀,在函数名后加上"@"和参数的字节数
4, thiscall (本身调用,仅用于“C++”成员函数)
1)压栈顺序:this指针存放于CX/ECX寄存器中,参数从右到左的压栈顺序
2)thiscall不是关键词,因此不能被程序员指定
如果参数个数确定,this指针通过ecx传递给被调用者;如果参数个数不确定,this指针
在所有参数压栈后被压入堆栈。
对参数个数不定的,调用者清理堆栈,否则函数自己清理堆栈
可见,对于参数个数固定情况下,它类似于stdcall,不定时则类似cdecl
5, naked call (裸调)
这是一个很少见的调用约定,一般程序设计者建议不要使用。 一般用于实模式驱动程序设计 .