三种函数调用约定
版权声明:本文为博主原创文章,转载请附上原文出处链接和本声明。2019-10-02,22:48:24。
作者By-----溺心与沉浮----博客园
综述
函数的调用约定就是在函数调用的时候的一些规则,包括:
函数参数的压栈顺序,
- 由调用者还是被调用者把参数弹出栈,
- 以及产生函数修饰名的方法。
- 函数参数的压栈顺序就是,一个函数被调用时调用者需要给函数几个参数(根据函数定义),参数的传递是通过栈来进行的。调用者按照一定顺序将参数依次压入栈中供被调用函数使用。you调用者还是被调用这把参数弹出栈,就是指谁管理栈空间,完整的调用过程是:调用者先将自己的栈基址(EBP)压栈,再将目前的栈顶(ESP)赋值给EBP作为新的栈底,然后在新栈底的基础上开辟一段新的栈空间来作为被调用函数使用的栈空间。在返回时究竟是由调用者还是被调用这来清空这段压入了(已经使用完的)参数的栈空间。产生函数修饰名的方式就是指在编译中,调用某个函数会生成一个描述这个函数的字符串,这个字符串的格式根据不同的调用约定会不同。
各类调用约定小结
主要总结下__cdecl,__stdcall,__fastcall,__nakedcall,__thiscall,__pascal几种调用约定的原理。(naked在上一篇博文中)
- __cdecl
是C和C++默认的调用约定(无论你写与不写,都是__cdecl):
- 从右至左的顺序将参数压栈
- 由调用者维护参数内存栈
- 修饰名格式是在原函数名前加一个下划线,如_function
- __stdcall
C和C++标准调用方式,Win32 API中的函数一般是这种调用方式:
- 从右至左的顺序将参数压栈
- 被调用这管理参数内存栈,即函数返回之前自己清空栈
- 修饰名格式是在原函数名前加一个下划线,原函数名后加一个@,如_function@
- __fastcall
fastcall就是快速调用约定,通过寄存器传递参数,相比于去内存栈中读取参数,fastcall更快:
- 使用ECX和EDX寄存器传送前两个DWORD或更小的参数,其他参数依次从右至左入栈
- 被调用函数负责管理参数内存栈
- 修饰名格式是在原函数名前和后各加一个@,并且在后面跟函数参数的字节数,如@function@16
- __thiscall
C++类成员函数缺省的调用约定,在C++中,由于类成员有一个this指针的特殊性,thiscall必须特殊处理:
- 参数从右至左入栈
- 参数个数不确定的调用由调用者负责管理内存栈,否则由被调用者管理内存栈;如果参数个数确定,this指针通过ecx寄存器传给调用函数,否则在所有参数入栈后,this指针入栈传给被调用者
- __nakedcall
一般很少使用这种调用约定,在nakedcall调用约定下,编译器不会给这种函数增加初始化和清理代码,更特殊的是,你不能用return返回返回值,只能用插入汇编返回结果。
- __pascal
pascal调用约定和上面几种不同:
- 从左至右的顺序将参数压栈
- 被调用者管理参数栈