解读C++调用约定

来源:互联网 发布:社会和学校的区别知乎 编辑:程序博客网 时间:2024/06/04 18:09

我们都知道DLL的调用方式有两种,即所谓动态调用和静态调用。静态调用就是告诉编译器我需要某个DLL,然后把要用的函数声明都定义出来,然后在运行时调用这些函数,这种用法和静态库的用法相似。动态调用就是运行时使用LoadLibrary将一个DLL载入到运行时环境,然后通过GetProcAddress获取具体的函数指针然后调用。

然而动态调用依然要求在编译时就确定函数的原型,因为在C++中只有通过函数指针才能实现函数的调用。但是在某些情况下,DLL中函数的参数表和返回类型在编译时还不能确定,只有在运行时才能通过某种办法得到,那么这个时候如何调用DLL函数呢?这就是本文要解决的问题。

首先,在什么情况下有可能遇到这个问题。比如我现在正在做一脚本语言,需要允许脚本在运行时调用DLL函数,而脚本要调用什么DLL函数主程序是不知道的,但是脚本在调用之前会先给一个所需要调用的DLL函数的声明,该声明包括函数的返回类型、参数表和函数所在的DLL文件名和函数在DLL中的名称以及函数的调用约定。有了这些信息,主程序如何响应脚本的要求调用相关DLL呢?传统的方法要求在编译器知道所需调用的函数原型,这显然是不行的。那么,我们需要在运行时动态地把这些参数传给函数,然后调用之。这超出了C++的范围,因此需要用借助内嵌汇编实现。

通过使用汇编,我们绕过了C++的参数检查,从而可以直接调用一个函数地址,当然,我们需要保证所传参数个数与所调用函数的参数个数相同。

在编写代码之前,需要了解C++的函数调用约定。C++允许下面几种调用约定:__cdecl, __stdcall, __fastcal,__thiscall和__clrcall。__thiscall用于调用类成员函数,__clrcall为托管C++所用,而__fastcall则是将参数放在寄存器中传递。__thiscall用于访问对象成员函数,这不属于我们讨论范畴。__fastcall由于通过寄存器传递参数,需要函数调用者和被调用函数的配合才能实现,由于我们只能控制函数调用者,因此__fastcall的行为不能确定,因此也不属于我们讨论范畴之列,实际上__fastcall在程序中较少使用,更不会出现在dll的导出函数中。而__clrcall用于.Net,也不属于我们讨论之列。因此我们要关心的是__cdecl和__stdcall。__cdecl是C/C++的默认调用约定,即函数调用者在调用函数时先将函数的所有参数按从右到左的顺序依次压入堆栈,然后调用函数,最后函数调用者要负责将所有参数弹出堆栈。而__stdcall与__cdecl的不同之处在于__stdcall是由被调用函数将参数弹出堆栈的。

 

原创粉丝点击