extern "C" 与 __stdcall使用

来源:互联网 发布:linux下yum下载rpm包 编辑:程序博客网 时间:2024/05/16 05:59

xtern "C" __declspec (dllexport) char* __stdcall SQLFetch(const char* fieldname);

C語言中extern c
c与c++程序连接问题 它们之间的连接问题主要是因为c c++编绎器对函数名译码的方式不能所引起的,考虑下面两个函数 /* c*/ int strlen(char* string) { ... } //c++ int strlen(char* string) { ... } 两个函数完全一样。在c在函数是通过函数名来识别的,而在C++中,由于存在函数的重载问题,函数的识别方式通函数名,函数的返回类型,函数参数列表三者组合来完成的。因此上面两个相同的函数,经过C,C++编绎后会产生完全不同的名字。所以,如果把一个用c编绎器编绎的目标代码和一个用C++编绎器编绎的目标代码进行连接,就会出现连接失败的错误。 解决的方法是使用extern C,避免C++编绎器按照C++的方式去编绎C函数 在头文件中定义: extern "C" int strlen(char* string) 或 extern "C" { int strlen(char* string) } 当C编绎器遇到extern "C"的时候就用传统的C函数编译方法对该函数进行编译。由于C编绎器不认识extern "C"这个编绎指令,而程序员又希望C,C++程序能共用这个头文件,因此通常在头文件中使用_cplusplus宏进行区分: #if define _cplusplus extern "C"{ #endif int strlen(char* string) #ifdefine _cplusplus } #endif

#define CALLBACK     __stdcall
#define WINAPI       __stdcall
那么,除了__stdcall,还有别的调用类型吗?究竟什么是调用类型呢?我的理解是:调用类型就是如何使用函数参数的一种规则。有三种调用类型:__fastcall、__cdecl、__stdcall。
1、__cdecl调用类型:
    这是C的调用规则。对于所有非C++成员函数或未标有__stdcall或__fastcall的函数来说,这是默认调用规则。
2、__fastcall调用类型:
    从字面意思可知,这是一种快速调用。因为CPU的寄存器会被使用来存放函数参数列表中的头几个参数。而剩下参数将被从右至左地推倒堆栈上。被调用函数将从 寄存器和堆栈获得函数参数。在x86中,ECX和EDX一般被用来存放开始的参数。在.NET中,为了性能上的快速,就是使用ecx和edx来实现 __fastcall的。
3、 __stdcall调用类型:
    该调用只是通过堆栈来push和pop参数。push参数时,顺序是从右到左。

现在,你应该明白了吧。最后,我带一句。三种调用类型在VC编译器中对应/Gd、/Gr、/Gz三个编译选项

调用C/C++制做的DLL文件中导出函数的几点说明

用某种语言调用C/C++制做的DLL文件中导出函数时,有几点注意:

一、“_stdcall”的作用
在C/C++中函数默认Calling Conventions(调用约定)是:
参数由右向左压入栈,由调用者清空栈。,
在FORTRAN、PASCAL、VisualBASIC等语言中,函数的Calling Conventions是:
参数由右向左压入栈,由被调函数清空栈。

那么在C/C++中使用_stdcall声明导出函数,就可以指定C/C++按照FORTRAN等的调用约定申明该函数,这时FORTRAN等其它语言中再调用这些函数就不会出现报错:run-time '49':Bad DLL call conventions.

二、加“extern "C"”的作用    
    extern "C" int _stdcall Extest();
    如上的原型,函数名符号会被VC编译器处理为
    int _stdcall Extest();
    如上的原型,函数名符号会被VC编译器处理为
    C语言的编译链接环境不能处理第二种方式的修饰名,所以如果你的DLL要在C和C++的环境下都适用,那么应该使用第一种命名方式。

例如:在MFC的类中调用自己写的C函数,有时出现错误说无法找到函数的定义,原因是由于C 和C++的编译器对函数的修饰名不同,C++的修饰名中包括了各参数类型,因此通常情况下,C++程序无法找到C库中的函数,需要在声明C函数时加上 extern "C"的说明:

extern"C"void foo();
C++编译器就会用C的修饰名方式来进行连接调用。同样,当C需要调用C++函数时,该C++函数也必须声明为extern "C"。通常可以在C的头文件里这样定义:

#ifdef __cplusplusextern"C" {#endif ...#ifdef __cplusplus }#endif

就可兼容C和C++程序。

原创粉丝点击