1. __cdecl
__cdecl 是C Declaration的缩写(declaration,声明),表示C语言默认的函数调用方法:所有参数从右到左依次入栈,由调用者负责把参数压入栈,最后也是由调用者负责清除栈的内容,一般来说,这是 C/C++ 的默认调用函数的规则,MS VC 编译器采用的规则则是这种规则
2. __stdcall
_stdcall 是StandardCall的缩写,是C++的标准调用方式:所有参数从右到左依次入栈,由调用者负责把参数压入栈,最后由被调用者负责清除栈的内容,Windows API 所采用的函数调用规则则是这种规则
另外,采用 __cdecl 和 __stdcall 不同规则的函数所生成的修饰名也各为不同,相同点则是生成的函数修饰名前缀都带有下划线,不同的则是后缀部分,当然,这两者最大的不同点就在于恢复栈的方式不同,而且这点亦是最为重要的。
__cdecl 规则要求调用者本身负责栈的恢复工作,在汇编的角度上说,恢复堆栈的位置是在调用函数内,考虑这样一段 C++ 代码(在 VC 下 Debug)
#include <cstdio> void __cdecl func(int param1, int param2, int param3) { int var1 = param1; int var2 = param2; int var3 = param3; printf("%ld\n", long(¶m1)); printf("%ld\n", long(¶m2)); printf("%ld\n", long(¶m3)); printf("----------------\n"); printf("%ld\n", long(&var1)); printf("%ld\n", long(&var2)); printf("%ld\n", long(&var3)); return ; } int main() { func(1, 2, 3); return 0; }
注意到 func 函数使用了 __cdecl 进行修饰(其实不需要,因为 VC 下默认的是 __cdecl 规则, 这里是为了更为清晰),生成汇编代码如下:
3: void __cdecl func(int param1, int param2, int param3) { 00401020 push ebp 00401021 mov ebp,esp 00401023 sub esp,4Ch 00401026 push ebx 00401027 push esi 00401028 push edi 00401029 lea edi,[ebp-4Ch] 0040102C mov ecx,13h 00401031 mov eax,0CCCCCCCCh 00401036 rep stos dword ptr [edi] 4: int var1 = param1; 00401038 mov eax,dword ptr [ebp+8] 0040103B mov dword ptr [ebp-4],eax ; 注意var1,var2,var3 压入堆栈的顺序! 5: int var2 = param2; 0040103E mov ecx,dword ptr [ebp+0Ch] 00401041 mov dword ptr [ebp-8],ecx 6: int var3 = param3; 00401044 mov edx,dword ptr [ebp+10h] 00401047 mov dword ptr [ebp-0Ch],edx ............................................... ; 省略了printf的代码 15: return ; 16: } 004010BD pop edi 004010BE pop esi 004010BF pop ebx 004010C0 add esp,4Ch 004010C3 cmp ebp,esp 004010C5 call __chkesp (004011d0) 004010CA mov esp,ebp 004010CC pop ebp 004010CD ret ; 这里是 ret,由调用者(main)恢复堆栈,但如果是 __stdcall 的话, ; 恢复堆栈就在这里进行 ******************************************************************************************************************* 18: int main() { ............................................... ; 省略了建立堆栈的代码 19: func(1, 2, 3); 00401118 push 3 ; 将 param3 压入栈 0040111A push 2 ; 将 param2 压入栈 0040111C push 1 ; 将 param1 压入栈 0040111E call @ILT+5(func) (0040100a) ; @ILT+5(func) 是函数func的修饰名,而0040100a则是他的地址 00401123 add esp,0Ch ; 恢复堆栈,__cdecl 规则由调用者(这里是main)恢复堆栈 20: return 0; 00401126 xor eax,eax 21: } 00401128 pop edi 00401129 pop esi 0040112A pop ebx 0040112B add esp,40h 0040112E cmp ebp,esp 00401130 call __chkesp (004011d0) 00401135 mov esp,ebp 00401137 pop ebp 00401138 ret
结果截图
程序中的栈结构如下图示:
__stdcall 规则由被调用者本身去调整堆栈,在汇编的角度上说,恢复堆栈的行为发生在调用者函数内,考虑这样一段代码(VC 下Debug):
#include <cstdio> void __stdcall func(int param1, int param2, int param3) { int var1 = param1; int var2 = param2; int var3 = param3; printf("%ld\n", long(¶m1)); printf("%ld\n", long(¶m2)); printf("%ld\n", long(¶m3)); printf("----------------\n"); printf("%ld\n", long(&var1)); printf("%ld\n", long(&var2)); printf("%ld\n", long(&var3)); return ; } int main() { func(1, 2, 3); return 0; }
注意到 func 函数使用了 __cdecl 进行修饰(其实不需要,因为 VC 下默认的是 __cdecl 规则, 这里是为了更为清晰),生成汇编代码如下:
1: #include <cstdio> 2: 3: void __stdcall func(int param1, int param2, int param3) { 00401020 push ebp 00401021 mov ebp,esp 00401023 sub esp,4Ch 00401026 push ebx 00401027 push esi 00401028 push edi 00401029 lea edi,[ebp-4Ch] 0040102C mov ecx,13h 00401031 mov eax,0CCCCCCCCh 00401036 rep stos dword ptr [edi] 4: int var1 = param1; 00401038 mov eax,dword ptr [ebp+8] 0040103B mov dword ptr [ebp-4],eax 5: int var2 = param2; 0040103E mov ecx,dword ptr [ebp+0Ch] 00401041 mov dword ptr [ebp-8],ecx 6: int var3 = param3; 00401044 mov edx,dword ptr [ebp+10h] 00401047 mov dword ptr [ebp-0Ch],edx .............................................. ; 省略 printf 代码 15: return ; 16: } 004010BD pop edi 004010BE pop esi 004010BF pop ebx 004010C0 add esp,4Ch 004010C3 cmp ebp,esp 004010C5 call __chkesp (004011d0) 004010CA mov esp,ebp 004010CC pop ebp 004010CD ret 0Ch ; __stdcall 在这里(被调用函数)恢复堆栈,但如果是 __cdecl 的话,这里是 ret, ; 堆栈的恢复由调用者(这里是 main)来负责 ******************************************************************************************************************* 18: int main() { ........................................... ; 省略建立堆栈代码 19: func(1, 2, 3); 00401118 push 3 ; param3 压入堆栈 0040111A push 2 ; param2 压入堆栈 0040111C push 1 ; param1 压入堆栈 0040111E call @ILT+0(func) (00401005) ; @ILT+0(func) 是函数的修饰名,而 00401005 则是调用函数func的地址 20: return 0; 00401123 xor eax,eax 21: } 00401125 pop edi 00401126 pop esi 00401127 pop ebx 00401128 add esp,40h 0040112B cmp ebp,esp 0040112D call __chkesp (004011d0) 00401132 mov esp,ebp 00401134 pop ebp 00401135 ret
运行的结果与使用 __cdecl 规则的一样,两者的栈结构基本一致的,唯一的不同就是调整堆栈(恢复堆栈)的位置以及生成函数的修饰名不同,而这样的不同正是 __stdcall 和 __cdecl 最为重要的不同点
相关推荐
- **返回值**:与 __cdecl 和 __stdcall 类似,返回值通常存储在 EAX 寄存器中。 - **修饰名格式**:函数名修饰为 `@functionname@number` 形式。 #### 应用场景: - **性能敏感的代码**:当函数调用频繁且对性能有...
本文将详细讲解两种常见的函数调用约定:__stdcall和__cdecl。 首先,__stdcall是C++中的标准调用方式,它的主要特征是参数从右到左入栈,即参数在函数调用时逆序放入堆栈。当函数返回时,使用`retn x`指令来清理...
_stdcall、_cdecl和_fastcall 的区别.zip
总结来说,`__stdcall` 和 `__cdecl` 的主要区别在于参数压栈的顺序和栈的清理责任。`__cdecl`是调用者清理栈,更灵活,适用于通用的函数;而`__stdcall`是被调用者清理栈,更高效,通常用于API函数或性能关键的代码...
易语言cdecl回调处理源码,cdecl回调处理,stdcall_to_cdecl,stdcall_to_cdecl_free,回调函数,test,VirtualAlloc,VirtualFree,set_data
__stdcall、__cdecl和__fastcall各有优势和应用场景,选择合适的调用约定对于优化代码性能和提高程序的可维护性至关重要。了解这些调用约定的具体细节有助于开发者更好地理解底层机制,并能够根据项目需求做出最佳...
标题中的“TEST DLL (__stdcall)”指的是一个关于创建和使用使用`__stdcall`调用约定的动态链接库(DLL)的技术主题。在Windows操作系统中,DLL是一种可共享的代码库,程序可以在运行时调用其中的函数来实现特定...
总结来说,stdcall和cdecl的主要区别在于参数清理的责任方:stdcall由被调用者负责,cdecl由调用者负责。选择哪种调用约定取决于具体的应用场景,通常系统级接口、API函数倾向于使用stdcall以提高效率,而一般的C/...
以下是一个详细的步骤,说明如何在Linux环境下创建、操作和加载动态库,特别是针对ARM平台。 首先,你需要确保你的开发环境已经准备就绪,包括C/C++编译器(如GCC)以及必要的交叉编译工具链。对于ARM平台,你需要...
stdcall调用约定是默认的调用约定,但也可以使用cdecl调用约定。 2. CharSet字段:该字段用于控制字符串参数的传递方式。如果CharSet字段设置为Unicode,则字符串参数将被转换为Unicode字符열;如果CharSet字段设置...
"stdcall介绍" stdcall 调用约定是高级语言中的一种函数调用约定,用于解决函数调用时参数传递问题。...stdcall 调用约定和 cdecl 调用约定都是高级语言中常用的函数调用约定,它们的特点和使用方法都是非常不同的。
本文将详细介绍两种常见的函数调用约定:stdcall 和 cdecl,并探讨它们之间的差异及其应用场景。 #### 1. 基础概念介绍 - **cdecl (Caller-Destructor)**:此调用约定是C语言和大多数C++函数使用的默认约定。在...
接下来,我们将通过具体的示例代码来进一步分析`__stdcall`和`__cdecl`的区别: ```c #include "stdio.h" // __stdcall 示例 void __stdcall f_stdcall(int a, int b) { int c; c = a + b; } // __cdecl 示例 ...
包含 Java 通过 RXTXComm 读写串口数据需要的动态链接库:SuperComSCL2008.Dll、rxtxSerial.dll、rxtxParallel.dll 和操作 SCL2008 显示屏的动态链接库 SCL_API_stdcall.dll、SCL_API_cdecl.dll
本文将深入探讨三种常见的函数调用约定:`__stdcall`、`__cdecl` 和 `__fastcall`,并对比它们的特点和适用场景。 首先,`__cdecl` 是C语言默认的调用约定,也是大多数编译器默认使用的约定。在`__cdecl`中,函数的...
C++builder调用VCdll.pdf 本文档详细介绍了C++builder调用VC编写的DLL的过程,并对__cdecl、__fastcall、__stdcall三种调用...同时,我们也可以了解到__cdecl、__fastcall、__stdcall三种调用约定的区别和使用方法。
### _stdcall与其它调用约定的区别 - **cdecl**:参数同样是从右到左压栈,但由被调用者清理栈空间。 - **fastcall**:前两个32位或64位的参数分别存储在ECX和EDX寄存器中,其余参数从右到左压栈,由调用者清理栈...