_stdcall 与 _cdecl

来源:互联网 发布:云计算峰会的资金来源 编辑:程序博客网 时间:2024/05/20 06:24
        几乎我们写的每一个WINDOWS API函数都是__stdcall类型的,首先,需要了解两者之间的区别: WINDOWS的函数调用时需要用到栈(STACK,一种先入后出的存储结构)。当函数调用完成后,栈需要清除,这里就是问题的关键,如何清除??

        如果我们的函数使用了_cdecl,那么栈的清除工作是由调用者,用COM的术语来讲就是客户来完成的。这样带来了一个棘手的问题,不同的编译器产生栈的方式不尽相同,那么调用者能否正常的完成清除工作呢?答案是不能。

        如果使用__stdcall,上面的问题就解决了,函数自己解决清除工作。所以,在跨(开发)平台的调用中,我们都使用__stdcall(虽然有时是以WINAPI的样子出现)。

        那么为什么还需要_cdecl呢?当我们遇到这样的函数如fprintf()它的参数是可变的,不定长的,被调用者事先无法知道参数的长度,事后的清除工作也无法正常的进行,因此,这种情况我们只能使用_cdecl。到这里我们有一个结论,如果你的程序中没有涉及可变参数,最好使用__stdcall关键字。

函数声明后面加个stdcall是什么意思
     在Win32汇编中,我们经常要和API打交道,另外也会常常使用自己编制的类似于API的带参数的子程序,本文要讲述的是在子程序调用的过程中进行参数传递的概念和分析。一般在程序中,参数的传递是通过堆栈进行的,也就是说,调用者把要传递给子程序(或者被调用者)的参数压入堆栈,子程序在堆栈取出相应的值再使用,比如说,如果你要调用 SubRouting(Var1,Var2,Var3),编译后的最终代码可是     
  push   Var3  
  push   Var2  
  push   Var1  
  call   SubRouting  
  add   esp,12  
   
      也就是说,调用者首先把参数压入堆栈,然后调用子程序,在完成后,由于堆栈中先前压入的数不再有用,调用者或者被调用者必须有一方把堆栈指针修正到调用前的状态。参数是最右边的先入堆栈还是最左边的先入堆栈、还有由调用者还是被调用者来修正堆栈都必须有个约定,不然就会产生不正确的结果,而Stdcall就是参数从右到左压入栈,由被调用的子程序来修正堆栈的指针。
Directive Parameter   order Clean-up Passes   parameters   in   registers?  
  register Left-to-right Routine Yes  
  pascal Left-to-right Routine No  
  cdecl Right-to-left Caller No  
  stdcall Right-to-left Routine No  
  safecall Right-to-left Routine No
请问__stdcall是什么意思啊,怎样使用呢。
网友回答:
发表者:yjh1982

msdn

发表者:zfbt

_stdcall是一种函数调用习惯!说明函数的参数是从右往左进栈的!  
  用法:放在函数声明前

发表者:sevencat

而且好像是调用者自己清stack的

发表者:dancingcalf

 
  看msdn中有关vc的函数调用修饰词解说的

发表者:sevencat

int   func(int   x)  
  int   func(int   x,...)  
  不知道我有没有记错。  
  前一个各种调用方式都可以。  
  而后一个只能用c调用方式,因为被调用函数并不知道调用者向stack里面压了多少参数。

发表者:harmonious

调用约定    
       
  调用约定(calling   convention)决定以下内容:函数参数的压栈顺序,由调用者还是被    
  调用者把参数弹出栈,以及产生函数修饰名的方法。mfc支持以下调用约定:    
       
       
  1、_cdecl    
       
  按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于“c”函数或者变量,修饰    
  名是在函数名前加下划线。对于“c++”函数,有所不同。    
       
  如函数void   test(void)的修饰名是_test;对于不属于一个类的“c++”全局函数,修饰    
  名是?test@@zaxxz。    
       
  这是mfc缺省调用约定。由于是调用者负责把参数弹出栈,所以可以给函数定义个数不定    
  的参数,如printf函数。    
       
       
  2、_stdcall    
       
  按从右至左的顺序压参数入栈,由被调用者把参数弹出栈。对于“c”函数或者变量,修    
  饰名以下划线为前缀,然后是函数名,然后是符号“@”及参数的字节数,如函数int   f    
  unc(int   a,   double   b)的修饰名是_func@12。对于“c++”函数,则有所不同。所有的w    
  in32   api函数都遵循该约定。    
       
  3、_fastcall    
       
  头两个dword类型或者占更少字节的参数被放入ecx和edx寄存器,其他剩下的参数按从右    
  到左的顺序压入栈。由被调用者把参数弹出栈,对于“c”函数或者变量,修饰名以“@    
  ”为前缀,然后是函数名,接着是符号“@”及参数的字节数,如函数int   func(int   a,    
    double   b)的修饰名是@func@12。对于“c++”函数,有所不同。未来的编译器可能使用    
  不同的寄存器来存放参数。    
       
  4、thiscall    
       
  仅仅应用于“c++”成员函数。this指针存放于cx寄存器,参数从右到左压栈。thiscal    
  l不是关键词,因此不能被程序员指定。    
       
       
  5、naked   call    
       
  采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存esi,edi,    
  ebx,ebp寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked   call不产生这 样的代码。    
       
  naked   call不是类型修饰符,故必须和_declspec共同使用,如下:    
       
  __declspec(   naked   )   int   func(   formal_parameters   )    
  {    
  //   function   body    
  }    
       
  在mfc中有以下宏定义:    
       
  #define   callback   __stdcall    
  #define   winapi   __stdcall    
  #define   winapiv   __cdecl    
  #define   apientry   winapi    
  #define   apiprivate   __stdcall    
  #define   pascal   __stdcall

原创粉丝点击