天天看点

【C/C++学习笔记】C++ 调用约定一、函数调用约定

一、函数调用约定

  首先,我们由函数的调用约定说起,microsoft的vc默认的是__cdecl方式,而windows API则是__stdcall,如果用vc开发dll给其他语言用,则应该指定__stdcall方式。堆栈由谁清除这个很重要,如果是要写汇编函数给C调用,一定要小心堆栈的清除工作,如果是__cdecl方式的函数,则函数本身(如果不用汇编写)则不需要关心保存参数的堆栈的清除,但是如果是__stdcall的规则,一定要在函数退出(ret)前恢复堆栈。

windows有如下五种调用约定:

1.__cdecl

       所谓的C调用规则。按从右至左的顺序压参数入栈,由调用者把参数弹出栈。切记:对于传送参数的内存栈是由调用者来维护的。返回值在EAX中因此,对于象printf这样变参数的函数必须用这种规则。编译器在编译的时候对这种调用规则的函数生成修饰名的饿时候,仅在输出函数名前加上一个下划线前缀,格式为_functionname。

2.__stdcall 

        按从右至左的顺序压参数入栈,由被调用者把参数弹出栈。_stdcall是Pascal程序的缺省调用方式,通常用于Win32 Api中,切记:函数自己在退出时清空堆栈,返回值在EAX中。  __stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数,格式为[email protected]。如函数int func(int a, double b)的修饰名是[email protected]。

3.__fastcall

       __fastcall调用的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈)。__fastcall调用约定在输出函数名前加上一个“@”符号,后面也是一个“@”符号和其参数的字节数,格式为@[email protected]。这个和__stdcall很象,唯一差别就是头两个参数通过寄存器传送。注意通过寄存器传送的两个参数是从左向右的,即第一个参数进ECX,第2个进EDX,其他参数是从右向左的入stack。返回仍然通过EAX。

4.__pascal

       这种规则从左向右传递参数,通过EAX返回,堆栈由被调用者清除。

5.__thiscall

仅仅应用于"C++"成员函数。this指针存放于CX寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。

二、__cdecl,__stdcall(WINAPI),__fastcall 的区别

(1) __cdecl:C/C++默认方式,参数从右向左入栈,主调函数负责栈平衡。

(2) __stdcall:windows API默认方式,参数从右向左入栈,被调函数负责栈平衡。

(3) __fastcall:快速调用方式。数优先从寄存器传入(ECX和EDX),剩下的参数再从右向左从栈传入。

三、最为关键的原则

(1) 导出的函数明确指定调用约定,一般为__stdcall

(2) 导出函数,跟调用者保持一致调用约定

(3) 调用约定错误如下(发生如下错误时检查调用约定):

Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.

继续阅读