调用方式__cdecl和__stdcall的异同点
来源:互联网 发布:mac有自带画图吗 编辑:程序博客网 时间:2024/04/28 21:57
我们最常用的调用约定有以下2种,__cdecl和__stdcall, __cdecl 是c/c++的默认调用约定(calling convention), __stdcall是windows api 函数的调用约定。这2种调用约定的参数传递方式是一样的, 都是从右至左; 在堆栈的维护方式上, __cdecl要求调用者清除堆栈, 而__stdcall由被调用函数自己清除堆栈;名称修饰上,__cdecl直接在原有的函数名称上加一个下划线_,而__stdcall方式不仅加下划线而且还在后面加一个“@参数占用字节数”,具体如何,请看下面的例子。
#include <stdio.h>
int __cdecl funcA(int a, int b);
int __stdcall funcB(int a, int b);
int main()
{
int a, b, c, d;
a = 3;
b = 4;
c = funcA(a, b);
d = funcB(a, b);
return 0;
}
int __cdecl funcA(int a, int b)
{
return a + b;
}
int __stdcall funcB(int a, int b)
{
return a + b;
}
上面2个函数funcA和funcB, 一个采用__cdecl调用方式,一个采用__stdcall调用方式,在main函数中,我们来调用这2个函数,看看产生的汇编代码有什么不同:
11: int a, b, c, d;
12:
13: a = 3;
00401048 mov dword ptr [ebp-4],3
14: b = 4;
0040104F mov dword ptr [ebp-8],4
15:
16: c = funcA(a, b); //__cdecl 调用方式
00401056 mov eax,dword ptr [ebp-8]
00401059 push eax
0040105A mov ecx,dword ptr [ebp-4]
0040105D push ecx
0040105E call @ILT+0(_funcA) (00401005)
00401063 add esp,8 // 由调用者main平衡堆栈
00401066 mov dword ptr [ebp-0Ch],eax
17: d = funcB(a, b); //__stdcall调用方式
00401069 mov edx,dword ptr [ebp-8]
0040106C push edx
0040106D mov eax,dword ptr [ebp-4]
00401070 push eax
00401071 call @ILT+10(_funcB@8) (0040100f)
00401076 mov dword ptr [ebp-10h],eax
18:
19: return 0;
00401079 xor eax,eax
堆栈:
……
a
Ebp - 4
b
Ebp – 8
c
Ebp – 0Ch
d
Ebp - 10h
…..
由汇编代码可以看出,程序定义的四个local变量a,b,c,d分别对应于栈空间的ebp-4,
ebp-8, ebp-0Ch, ebp-10h
程序中a=3, 这个赋值语句对应的汇编代码是
mov dword ptr[ebp-4], 3
同样,赋值语句b=4, 对应的汇编代码是
mov dword ptr[ebp-8], 4
函数funcA调用后的返回值保存在c中,
mov dword ptr[ebp-0Ch], eax
函数funcB调用后的返回值保存在d中,
mov dword ptr[ebp-10h], eax
现在看看__cdecl调用方式的特点:
从参数传递上来看:
c=funcA(a,b);
对应汇编:
mov eax,dword ptr[ebp-8]
pusheax ;压入参数b
mov ecx,dword ptr[ebp-4]
pushecx ;压入参数a
从这里的汇编可以很明显的看出, 先传入参数b,然后传入参数a,即参数传递由右到左
从堆栈清除来看:
call @ILT+0(_funcA) (00401005)
add esp,8
函数funcA调用之后,main函数又用了一句代码add esp, 8来恢复堆栈,即__cdecl调用方式是由调用者清除堆栈的
从函数修饰来看:
call @ILT+0(_funcA) (00401005)
从这里可以看出,原有的函数funcA被修改成了_funcA,即__cdecl调用方式是直接在原有的函数前面加一个下划线来修饰的。
接着,我们来看看__stdcall方式调用的特点:
d =funcB(a, b);
从参数传递来看:
mov edx,dwordptr [ebp-8]
push edx ;传入参数b
mov eax,dwordptr [ebp-4]
push eax ;传入参数a
很明显,跟__cdecl调用方式一样,参数都是从右到左的
从清除堆栈来看:
call @ILT+10(_funcB@8) (0040100f)
函数调用之后,并没有任何清除堆栈的操作,而堆栈肯定是要保持平衡的,主函数main里面没有清除堆栈的操作,那么也就意味着堆栈的清除是由被调用函数自己做的, 即__stdcall调用方式,由被调用函数自己清除堆栈
从函数修饰来看:
call @ILT+10(_funcB@8) (0040100f)
函数被修改成了_funcB@8, 即修饰后的函数是原有的函数的前面加上了下划线,后面加上参数所占空间大小,中间用@隔开。因为有2个参数int a, int b,而sizeof(int)=4, 2个加起来等于8。如果是这样int funcB(int a, double 8), 则修饰后的函数是_funcB@12, 因为sizeof(int)+sizeeof(double)=4+8=12。
把__cdecl调用方式和__stdcall调用方式用表格总结一下:
调用方式
参数传递
堆栈清除
函数修饰
__cdecl
从右到左
调用者清除堆栈
funcA(int a, int b); =>_funcA
__stdcall
从右到左
被调用者自己清除堆栈
funcB(int a, int b);
=> _funcB@8
__cdecl因为是由调用者清除堆栈的,故可以使用可变参数,最明显的一个例子就是printf函数,采用的就是__cdecl调用方式, 同时由于__cdecl是调用者清除堆栈,所以,每当调用一个函数,都会增加清除堆栈的代码,故产生的最终代码要比__stdcall方式要大,占用磁盘空间也大。
<script type="text/javascript"><!-- google_ad_client = "pub-8062742131839416";/* 728x90, created 7/12/09 */google_ad_slot = "9294340931";google_ad_width = 728;google_ad_height = 90;// --></script><script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script>
- 调用方式__cdecl和__stdcall的异同点
- 关于__stdcall和__cdecl调用方式的理解
- 关于__stdcall和__cdecl调用方式的理解
- 关于__stdcall和__cdecl调用方式的理解
- 关于__stdcall和__cdecl调用方式的理解
- 关于__stdcall和__cdecl调用方式的理解
- 有关__cdecl和__stdcall调用方式
- 关于函数调用方式`__stdcall`和`__cdecl`
- 【C/C++】关于编译修饰符__stdcall、__cdecl和__fastcall 的异同点和应用场景
- 函数调用方式“__stdcall”,"__cdecl"
- 反汇编分析__stdcall和__cdecl的异同
- 反汇编分析__stdcall和__cdecl的异同
- 函数调用方式的区别[thiscall,__cdecl,__stdcall]
- 函数调用方式的区别[thiscall,__cdecl,__stdcall]
- 函数调用方式的区别[thiscall,__cdecl,__stdcall]
- 【转】函数调用方式的区别[thiscall,__cdecl,__stdcall]
- 函数调用方式的区别[thiscall,__cdecl,__stdcall]
- 函数调用方式的区别[thiscall,__cdecl,__stdcall]
- Prototype 学习——Date对象
- Ubuntu 8.10 下移植 madplay 到 mini2440
- saveOrupdate方法如何使用 (转帖网址:http://www.javaeye.com/topic/2632)
- ojhb/
- 一日一练(1)
- 调用方式__cdecl和__stdcall的异同点
- Java Table Examples
- Virtual Keys, Standard Set
- 解决Ubuntu9.04的VMware tools 鼠标和共享文件夹问题
- 视频编码标准H.264的核心技术分析
- Java read file
- .NET : Func委托和Action委托
- 纯手写bmp图片
- PE文件结构分析