stdcall与cdecl的区别
来源:互联网 发布:网络鉴黄师招聘信息 编辑:程序博客网 时间:2024/05/10 19:48
1 区别
VC++的C/C++函数有两种基本的调用约定:__stdcall、__cdecl。它们有什么区别呢?请参考下表: __stdcall __cdecl 函数代码 C int __stdcall addS(int a,int b) { return a + b; } int __cdecl addC(int a,int b) { return a + b; } ASM32 push ebp mov ebp,esp sub esp,40h push ebx push esi push edi lea edi,[ebp-40h] mov ecx,10h mov eax,0CCCCCCCCh rep stos dword ptr [edi] mov eax,dword ptr [ebp+8] add eax,dword ptr [ebp+0Ch] pop edi pop esi pop ebx mov esp,ebp pop ebp ret 8 push ebp mov ebp,esp sub esp,40h push ebx push esi push edi lea edi,[ebp-40h] mov ecx,10h mov eax,0CCCCCCCCh rep stos dword ptr [edi] mov eax,dword ptr [ebp+8] add eax,dword ptr [ebp+0Ch] pop edi pop esi pop ebx mov esp,ebp pop ebp ret 函数调用 C addS(1,2); addC(1,2); ASM32 push 2 push 1 call @ILT+85(addS) push 2 push 1 call @ILT+50(addC) add esp,8
说明:
1、函数 addS、addC 的调用约定分别是__stdcall、__cdecl,它们32位汇编代码的不同之处仅仅在于ret 8和ret;
2、addS(1,2)与addC(1,2)的32位汇编代码不同之处在于后者多了一个add esp,8;
3、寄存器esp保存着程序堆栈的栈顶地址
调用addS(1,2)的过程是这样的:push 2、push 1用来把参数压入堆栈,压入了两个4字节的int,所以esp的数值会减去8。然后call语句调用addS的汇编代码。形参a的数值是dword ptr [ebp+8],即push 1压入的1;形参b的数值是dword ptr [ebp+0Ch],即push 2压入的2。ebp其实是esp的初始值(mov ebp,esp),所以参数a、b是取自堆栈的。addS返回时调用的是ret 8,亦即返回时会将寄存器esp的数值加上8,完成堆栈的清除工作。
调用addC(1,2)的过程与addS(1,2)大致相同,不同之处在于ret不会修改寄存器esp,不会完成堆栈的清除工作。在执行add esp,8时,才会将esp寄存器的数值加上8,完成堆栈的清除工作。
简单的说,就是:__stdcall的函数在返回时会自动清除堆栈中的参数;__cdecl的函数在返回时不会自动清除堆栈中的参数,清除工作由调用者完成。
2 函数指针
再看看下面的例子:
函数调用
C
int(__stdcall*pfnS)(int,int)=&addS;
(*pfnS)(1,2);
int(__cdecl*pfnC)(int,int)=&addC;
(*pfnC)(1,2);
ASM32
mov dword ptr [ebp-14h],offset @ILT+85(addS) (0040105a)
mov esi,esp
push 2
push 1
call dword ptr [ebp-14h]
cmp esi,esp
call _chkesp (00401f1e)
mov dword ptr [ebp-18h],offset @ILT+50(addC) (00401037)
mov esi,esp
push 2
push 1
call dword ptr [ebp-18h]
add esp,8
cmp esi,esp
call _chkesp (00401f1e)
调用(*pfnS)(1,2)比调用addS(1,2)多了如下代码:
mov esi,esp //保存寄存器esp的数值至寄存器esi
... ... ...
cmp esi,esp //查看寄存器esp的数值是否变化了
call _chkesp (00401f1e) //寄存器esp的数值变化了,提示出错
也就是说:通过函数指针调用函数,会检查寄存器esp的数值是否被正常恢复。
如果强制 pfnS 指向 addC,然后执行(*pfnS)(1,2);,会发生什么?请参考如下代码:
int(__stdcall*pfnS)(int,int)=(int(__stdcall*)(int,int))&addC;
(*pfnS)(1,2);
运行时会产生如下错误提示,说明寄存器 ESP 产生了错误:
- stdcall与cdecl的区别
- stdcall,cdecl,fastcall区别与联系
- cdecl和stdcall等函数调用约定(function call convention)的区别与联系
- Atitit 函数调用的原理与本质attilax总结 stdcall cdecl区别
- stdcall与cdecl以及fastcall区别(转载+整理)
- cdecl, stdcall, pascal,fastcall的区别和调用约定
- cdecl、stdcall、fastcall、declspec 的用法和区别
- cdecl、stdcall、fastcall、declspec 的用法和区别
- cdecl、stdcall、fastcall、declspec 的用法和区别
- cdecl、stdcall、fastcall、declspec 的用法和区别
- cdecl、stdcall、fastcall、declspec 的用法和区别
- cdecl、stdcall、fastcall、declspec 的用法和区别(转)
- cdecl、stdcall,pascal三种动态库的区别
- vc++中stdcall与cdecl的相关知识
- vc++中stdcall与cdecl的相关知识
- stdcall cdecl
- stdcall cdecl
- stdcall cdecl
- JAVASE学习(13)Swing
- python及其依赖安装
- 【Android】快速开发偷懒必备(二) 支持DataBinding啦~爽炸,一行实现花式列表[申明:来源于网络]
- eclipse插件安装
- android 怎么编写一个简单的聊天界面
- stdcall与cdecl的区别
- 高端人才的警言
- iOS 关于label上显示不同颜色的字体
- maven的配置依赖jar包查询地址
- 根据uri获取图片文件
- Qt 转发两串口数据(二、桌面程序版)
- 利用oracle官网提供的occi库在windows下操作oracle数据库
- 在CentOS7上源码安装MongoDB 3.2.7
- javascript:;与javascript:void(0)使用介绍