天天看点

动态链接库函数调用约定一、函数调用约定二、C语言编译器函数名称修饰规则 三、C++语言编译器函数名称修饰规则 四、动态链接库的处理五、函数的调用过程

一、函数调用约定

当参数个数多于一个时,按照什么顺序把参数压入堆栈函数调用后,由谁来把堆栈恢复原装。在高级语言中,通过函数调用约定来说明这两个问题。常见的调用约定有:

 stdcall,cdecl,fastcall,thiscall,naked call

__stdcall、__cdecl和__fastcall是三种函数调用协议,函数调用协议会影响函数参数的入栈方式、栈内数据的清除方式、编译器函数名的修饰规则等(区别主要体现在编译成的汇编语言上)。

__stdcall:Windows API默认的函数调用协议。

 __cdecl:C/C++默认的函数调用协议。 

__fastcall:适用于对性能要求较高的场合。

二、C语言编译器函数名称修饰规则 

__stdcall:编译后,函数名被修饰为"[email protected]"。 

__cdecl:编译后,函数名被修饰为"_functionname"。 

__fastcall:编译后, 函数名给修饰为"@[email protected]"。 

注:"functionname"为函数名,"number"为参数字节数。 

注:函数实现和函数定义时如果使用了不同的函数调用协议,则无法实现函数调用。

三、C++语言编译器函数名称修饰规则 

__stdcall:编译后,函数名被修饰为"?functionname@@YG******@Z"。 

__cdecl:编译后,函数名被修饰为"?functionname@@YA******@Z"。 

__fastcall:编译后,函数名被修饰为"?functionname@@YI******@Z"。 

注:"******"为函数返回值类型和参数类型表。

 注:函数实现和函数定义时如果使用了不同的函数调用协议,则无法实现函数调用。 C语言和C++语言间如果不进行特殊处理,也无法实现函数的互相调用。

四、动态链接库的处理

EAPI int add(int a, int b);

其中EAPI的定义如下:

#ifdef DYNAMIC_EXPORT

#define _API_ __declspec(dllexport)

#else

#define _API_ __declspec(dllimport)

#endif

#define EAPI extern "C" _API_
           

如果定义了DYNAMIC_EXPORT宏,则API表示导出接口,否则表示导入接口;

而 #define EAPIextern “C” API 

表示以C的方式导出(导入)接口。我们在这三个工程中都加入DYNAMIC_EXPORT预编译宏,表示导出接口;而在使用这三个工程(库)的工程(如VisualStudio)中不加DYNAMIC_EXPORT宏,表示导入接口。

五、函数的调用过程

要深入理解函数调用约定,你须要了解函数的调用过程和调用细节。 假设函数A调用函数B,我们称A函数为”调用者”,B函数为“被调用者”。如下面的代码,ShowResult为调用者,add为被调用者。

int add(int a, int b) 

{ 

 return a + b; 

} 

 void ShowResult() 

{ 

 std::cout << add(5, 10)<< std::endl; 

}
           

函数调用过程可以这么描述: 

(1)先将调用者(A)的堆栈的基址(ebp)入栈,以保存之前任务的信息。 

(2)然后将调用者(A)的栈顶指针(esp)的值赋给ebp,作为新的基址(即被调用者B的栈底)。 

(3)然后在这个基址(被调用者B的栈底)上开辟(一般用sub指令)相应的空间用作被调用者B的栈空间。 

(4)函数B返回后,从当前栈帧的ebp即恢复为调用者A的栈顶(esp),使栈顶恢复函数B被调用前的位置;然后调用者A再从恢复后的栈顶可弹出之前的ebp值(可以这么做是因为这个值在函数调用前一步被压入堆栈)。这样,ebp和esp就都恢复了调用函数B前的位置,也就是栈恢复函数B调用前的状态。 

此部分内容参考:http://blog.csdn.net/zsy2020314/article/details/9429707

六、 VS设置默认的调用约定

C/C++默认的调用约定是__cdecl,那能不能修改这个默认的调用约定呢? 答案是肯定的。假设你有一个工程名叫VisualStudio,你想让这个工程下的所有函数默认都使用__stdcall,右键工程->Properties->ConfigurationProperties->C/C++->Advanced->Calling Convention,将其设置为__stdcall即可。

参考链接:

https://blog.csdn.net/luoweifu/article/details/52425733

https://blog.csdn.net/luoweifu/article/details/52456407

继续阅读