(转载)函数调用方式比较

对于各种的call,看看汇编的结果很清楚,  例如对于函数:  
  void   func(int   a,   int   b,   int   c,   int   d)   {   }  
  使用函数如下  
  int   main()  
  {  
  func(1,   2,   3,   4);  
  }   

1.如果函数func是__cdecl(默认调用方式),调用时情况如下  
  int   main()  
  {  
  //参数从右到左压栈  
  push   4  
  push   3  
  push   2  
  push   1  
  call   func  
  add   esp   0x10   //调用者恢复堆栈指针esp,4个参数的大小是0x10(4x4)  
  }  
   
  2.如果函数func是__stdcall,调用时情况如下  
  int   main()  
  {  
  //参数从右到左压栈  
  push   4  
  push   3  
  push   2  
  push   1  
  call   func  
                //恢复堆栈指针由被调用者func负责,方法是"ret   0x10"  
  }  
   
  3.如果函数func是__pascal,调用情况如下  
  int   main()  
  {  
  //参数从左到右压栈  
  push   1  
  push   2  
  push   3  
  push   4  
  call   func  
  //恢复堆栈指针由被调用者func负责,方法是"ret   0x10"  
  }   
    
 4.如果函数func是__fastcall,调用情况如下  
  int   main()  
  {  
  //参数先用ecx,   edx,   eax传递,然后再压栈  
  //不进栈  
  //(不知为什么,帮助中写的是从左到右传递的,  
  //是不是错了,还是BCB6和BCB5的不一样)  
  push   4  
  mov   ecx 3  
  mov   edx 2  
  mov   eax 1  
  call   func  
  //恢复堆栈指针由被调用者func负责,方法是"ret   0x04",  
  //因为只进栈一个参数,其余用寄存器传递,所以用ret   0x04恢复  
  }   
    
   

函数调用规范__cdecl和__stdcall的区别一目了然(表格形式)

Posted on Tuesday, May 24, 2005 3:23 PM #C & C++ href="http://blog.bcchinese.net/happyjet/Services/Pingback.aspx" rel="pingback" />

 

__cdecl

 

 

__stdcall

 

C C++ 程序的缺省调用规范

 

为了使用这种调用规范,需要你明确的加上 __stdcall (或 WINAPI )文字。即 return-type __stdcall function-name[(argument-list)]

 

 

调用函数 (Callee) 返回,由调用方 (Caller) 调整堆栈。

 

1. 调用方的函数调用

                                         

2. 被调用函数的执行

 

3. 被调用函数的结果返回

 

4. 调用方清除调整堆栈

                                                                

 

调用函数 (Callee) 返回,由调用函数 (Callee) 调整堆栈。图示:

 

1. 调用方的函数调用

 

2. 被调用函数的执行

 

3. 被调用函数清除调整堆栈

 

4. 被调用函数的结果返回                                                           

 

因为每个调用的地方都需要生成一段调整堆栈的代码,所以最后生成的文件较大。

 

 

因为调整堆栈的代码只存在在一个地方(被调用函数的代码内),所以最后生成的文件较小。

 

函数的参数个数可变(就像 printf 函数一样),因为只有调用者才知道它传给被调用函数几个参数,才能在调用结束时适当地调整堆栈。

 

 

函数的参数个数不能是可变的。

 

对于定义在 C 程序文件中的输出函数,函数名会保持原样,不会被修饰。

对于定义在 C++ 程序文件中的输出函数,函数名会被修饰, MSDN Underscore character (_) is prefixed to names . 我实际测试( VC4 VC6 )下来发现好像不是那么简单。

可通过在前面加上 extern C 以去除函数名修饰。也可通过 .def 文件去除函数名修饰。

 

不论是 C 程序文件中的输出函数还是 C++ 程序文件中的输出函数,函数名都会被修饰。

对于定义在 C 程序文件中的输出函数, An underscore (_) is prefixed to the name. The name is followed by the at sign (@) followed by the number of bytes (in decimal) in the argument list.

对于定义在 C++ 程序文件中的输出函数,好像更复杂,和 __cdecl 的情况类似。

好像只能通过 .def 文件去除函数名修饰。

 

 

_beginthread 需要 __cdecl 的线程函数地址

 

 

_beginthreadex CreateThread 需要 __stdcall 的线程函数地址

 

 

两者的参数传递顺序都是从右向左。

为了让 VB 可以调用,需要用 __stdcall 调用规范来定义 C/C++ 函数。请参看Microsoft KB153586 文章:How To Call C Functions That Use the _cdecl Calling Convention

当你 LoadLibrary 一个 DLL 文件后, 把 GetProcAddress 取得的函数地址传给上面三个线程生成函数时,请务必确认实际定义在 DLL 文件的输出函数符合调用规范要求。否则,编译成 Release 版后运行,可能会破坏堆栈,程序行为不可预测。

VC 中的相关编译开关:/Gd /Gr /Gz。另外,VC6中新增加的 /GZ 编译开关可以帮你检查堆栈问题。

我也是初学者,若有不对的地方、可以补充的地方,请指教。谢谢。  

 

(补充)汇编语言视点的比较文章: Intel x86 Function-call Conventions - Assembly View


<script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值