c/c++ 函数调用的方式 __cdecl __stdcall __fastcall
参考链接:https://zhuanlan.zhihu.com/p/170134539
参考链接:https://baike.baidu.com/item/__cdecl/9518056
参考链接:https://blog.csdn.net/hellokandy/article/details/54603055
知乎的那个参考链接给出了三种方式的汇编区别,但是我自己用g++没有搞出来,回去用vs试一下。
在高级语言中,就是通过函数的调用方式来说明这两个问题的。常见的调用方式有:
C 语言: __cdecl、__stdcall、__fastcall、naked、__pascal。
C++ 语言: __cdecl、__stdcall、__fastcall、naked、__pascal、__thiscall,比 C 语言多出一种 __thiscall 调用方式。
C中不加说明默认函数为_cdecl方式(C中也只能用这种方式),C++也一样,但是默认的调用方式可以在IDE环境中设置。
1、stdcall
按从右至左的顺序压参数入栈。
由被调用者把参数弹出栈。切记:函数自己在退出时清空堆栈,返回值在EAX中。
__stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数,格式为_function@number。如函数int sub(int a, int b)的修饰名是_sub@8。
2、cdecl
(1)参数从右向左依次压入堆栈.
(2)由调用者恢复堆栈,称为手动清栈。
(3)函数名自动加前导下划线。
按从右至左的顺序压参数入栈、。
由调用者把参数弹出栈。切记:对于传送参数的内存栈是由调用者来维护的,返回值在EAX中。因此对于像printf这样可变参数的函数必须用这种约定。
编译器在编译的时候对这种调用规则的函数生成修饰名的时候,在输出函数名前加上一个下划线前缀,格式为_function。如函数int add(int a, int b)的修饰名是_add。
3、fastcall
实际上__fastcall用ECX和EDX传送前两个DWORD或更小的参数,剩下的参数仍自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈。
__fastcall调用约定在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,格式为@function@number,如double multi(double a, double b)的修饰名是@multi@16。
__fastcall和__stdcall很象,唯一差别就是头两个参数通过寄存器传送。注意通过寄存器传送的两个参数是从左向右的,即第1个参数进ECX,第2个进EDX,其他参数是从右向左的入栈,返回仍然通过EAX。
4、thiscall
(1)参数从右向左压入栈。
(2)如果参数个数确定,this指针通过ecx传递给被调用者;如果参数个数不确定,this指针在所有参数压入栈后被压入栈。参数个数不定的,由调用者清理堆栈,否则由函数自己清理堆栈。可以看到,对于参数个数固定的情况,它类似于stdcall,不定时则类似于cdecl。
5 naked call
采用前面几种函数调用约定的函数,编译器会在必要的时候自动在函数开始添加保存ESI,EDI,EBX,EBP寄存器的代码,在退出函数时恢复这些寄存器的内容,使用naked call方式声明的函数不会添加这样的代码,这也就是为什么称其为naked的原因吧。
naked call不是类型修饰符,故必须和_declspec共同使用。
要点 __cdecl __stdcall __fastcall 参数传递方式 右->左 右->左 左边开始的两个不大于4字节(DWORD)的参数分别放在ECX和EDX寄存器,其余的参数自右向左压栈传送 清理栈方 调用者清理 被调用函数清理 被调用函数清理 适用场合 C/C++、MFC的默认方式; 可变参数的时候使用; Win API 要求速度快 C编译修饰约定 _functionname _functionname@number @functionname@number