现在的位置: 首页 > 综合 > 正文

extern “C” __stdcall对函数的使用声明

2018年02月09日 ⁄ 综合 ⁄ 共 2664字 ⁄ 字号 评论关闭

extern "C"     __stdcall对函数的使用声明如下:

 

1    extern "C":

 

 

在当调用别人写的库时,注意库是使用何种编译器,若是C的,则你在用VC中的C++编译器调用时就得

   #if defined(__cplusplus)
   extern "C" {
#endif

 

 

..........声明被调用的函数名

 

 

#if defined(__cplusplus)
};
#endif

 这样就可以让C++使用因编译器不同导致的名称不同的函数!!!

 

 

  2    __stdcall:  (部分转载)

   

  __stdcall是最常用的方式(API)

 

__cdecl是C和C++程序的缺省调用方式

 

__stdcall __cdecl使用的意义在于:当我们使用动态连接库的时候,需要声明类型,在调用的时候类型要一致,否则会应压栈

等方式不同而可能错误如下:

   DLL中:

int  _declspec(dllexport) __stdcall  IDTS_BV(BVinfo *Bvinforma)

 

调用程序中:

 typedef int (*pIDTS_IDTS_SetBV)(BVinfo *Adinfo_para);//__stdcall 未添加

pIDTS_IDTS_SetBV  IDTS_SetBV;

DllHandle = LoadLibrary(m_DriverName);

IDTS_SetBV = (pIDTS_IDTS_SetBV) GetProcAddress(DllHandle, "IDTS_BV"));

调用   可能    出错!这两种压栈方式一样,而且只有一个变量 好像不出错!!!!!!!!!!!!!!!!!!!!

至于这种函数被调用,则和普通的cdecl及stdcall调用函数一致。函数调用约定导致的常见问题如果定义的约定和使用的约定不一致,则将导致堆栈被破坏,导致严重问题,下面是两种常见的问题:

函数原型声明和函数体定义不一致

DLL导入函数时声明了不同的函数约定

以后者为例,假设我们在dll种声明了一种函数为:

__declspec(dllexport) int func(int a,int b);//注意,这里没有stdcall,使用的是

cdecl

使用时代码为:

typedef int (*WINAPI DLLFUNC)func(int a,int b);

hLib = LoadLibrary(...);

DLLFUNC func = (DLLFUNC)GetProcAddress(...)//这里修改了调用约定

result = func(1,2);//导致错误

由于调用者没有理解WINAPI的含义错误的增加了这个修饰,上述代码必然导致堆栈被破坏,MFC在编译时插入的checkesp函数将告诉你,堆栈被破坏了。

 

每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用__stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。

       __stdcall是Pascal程序的缺省调用方式,通常用于Win32 Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数。

       __fastcall方式的函数采用寄存器传递参数,VC将函数编译后会在函数名前面加上"@"前缀,在函数名后加上"@"和参数的字节数。

 

   #define CALLBACK    __stdcall
    #define WINAPI      __stdcall
    #define WINAPIV     __cdecl
    #define APIENTRY    WINAPI
    #define APIPRIVATE __stdcall
    #define PASCAL      __stdcall
    #define cdecl _cdecl
    #ifndef CDECL
    #define CDECL _cdecl
    #endif

 

几乎我们写的每一个WINDOWS API函数都是__stdcall类型的,为什么??
    首先,我们谈一下两者之间的区别:
     WINDOWS的函数调用时需要用到栈(STACK,一种先入后出的存储结构)。当函数调用完成后,栈需要清楚,这里就是问题的关键,如何清除??
      如果我们的函数使用了_cdecl,那么栈的清除工作是由调用者,用COM的术语来讲就是客户来完成的。这样带来了一个棘手的问题,不同的编译器产生栈的方式不尽相同,那么调用者能否正常的完成清除工作呢?答案是不能。
      如果使用__stdcall,上面的问题就解决了,函数自己解决清除工作。所以,在跨(开发)平台的调用中,我们都使用__stdcall(虽然有时是以WINAPI的样子出现),如JNI。
      那么为什么还需要_cdecl呢?当我们遇到这样的函数如fprintf()它的参数是可变的,不定长的,被调用者事先无法知道参数的长度(如 typedef int (*MYPROC)(LPTSTR, ...); ),事后的清除工作也无法正常的进行,因此,这种情况我们只能使用_cdecl。
      到这里我们有一个结论,如果你的程序中没有涉及可变参数,最好使用__stdcall关键字。

   1.__cdecl
        这是编译器默认的函数调用转换方式,它可以处理可变参数的函数调用。参数
        的入栈顺序是从右向左。在函数运行结束后,由调用函数负责清理入栈的参数。
        在编译时,在每个函数前面加上下划线(_),没有函数名大小写的转换。即
                  _functionname

    2.__fastcall
        有一些函数调用的参数被放入ECX,EDX中,而其它参数从右向左入栈。被调用
        函数在它将要返回时负责清理入栈的参数。在内嵌汇编语言的时候,需要注意
        寄存器的使用,以免与编译器使用的产生冲突。函数名字的转换是:
                  @functionname@number
        没有函数名大小写的转换,number表示函数参数的字节数。由于有一些参数不
        需要入栈,所以这种转换方式会在一定程度上提高函数调用的速度。

    3.__stdcall
      函数参数从右向左入栈,被调用函数负责入栈参数的清理工作。函数名转换格
      式如下:
    

抱歉!评论已关闭.