stdcall与cdecl的区别
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, pascal 区别(转载)
转载自:http://www.cnblogs.com/lidabo/archive/2012/11/21/2781484.html stdcall, cdecl, pascal 区别 这三个参数都是告 ...
- stdcall、cdecl详解(以及WINAPI和CALLBACK之类的宏对应什么)
转自:http://blog.csdn.net/huanjieshuijing/article/details/5822942 对_stdcall 的理解在C语言中,假设我们有这样的一个函数:int ...
- stdcall、cdecl、fastcall、thiscall 、naked call的汇编详解
函数调用规范 当高级语言函数被编译成机器码时,有一个问题就必须解决:因为CPU没有办法知道一个函数调用需要多少个.什么样的参数.即计算机不知道怎么给这个函数传递参数,传递参数的工作必须由函数调用者 ...
- Java调用动态库方法说明-最详细
Java不能直接调用由c或者c++写得dll(TF_ID.dll),所以只能采用jni得方法,一步一步生成符合规范得dll文件(假设叫FANGJIAN.dll),在FANGJIAN.dll这个文件里来 ...
- c++中的几种函数调用约定(转)
C++中的函数调用约定(调用惯例)主要针对三个问题: 1.参数传递的方式(是否采用寄存器传递参数.采用哪个寄存器传递参数.参数压桟的顺序等): 参数的传递方式,最常见的是通过栈传递.函数的调用方将参数 ...
- Delphi中register, pascal, cdecl, stdcall, safecall(转)
源:http://blog.sina.com.cn/s/blog_552c78120100hsr9.html 注: 使用错误,或者在该加的地方没有加,可能会出现"privileged ins ...
- Object Pascal 过程与函数
过程与函数 过程与函数是实现一定功能的语句块,是程序中的特定功能单元.可以在程序的其他地方被调用,也可以进行递归调用.过程与函数的区别在于过程没有返回值,而函数有返回值. 1.过程与函数的定义 过程与 ...
- 【转】Delphi内嵌ASM简易教程
Delphi内嵌ASM简易教程 作者:heiying2006-03-19 18:33分类:默认分类标签: 前言 Delphi作为一个快速高效的开发平台,使用的人越来越多,但熟悉在Delphi代码中嵌入 ...
- Delphi代码中嵌入ASM代码
前言 Delphi作为一个快速高效的开发平台,使用的人越来越多,但熟悉在Delphi代码中嵌入ASM代码的程序员我想不多,因为这方面的资料太少了,另一方面,它还需要有基本的汇编语言知识,关於汇编语言的 ...
随机推荐
- codevs1137 计算系数
1137 计算系数 2011年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description 给定一 ...
- mvcAPI (入门 2)
1)建立一个实体类 using System; using System.Collections.Generic; using System.Linq; using System.Web; names ...
- 编译android源码官方教程(1)硬件、系统要求
https://source.android.com/source/requirements.html Requirements IN THIS DOCUMENT Hardware requireme ...
- Wpf再次学习,分享给入门的朋友
一.WPF介绍 先说下WPF,她的简称是Windows Presentation Foundation,注意到Presentation这个单词了吧,展现的意思,后面那个是基础,展现基础,WPF是一种展 ...
- :判断101-200之间有多少个素数,并输出所有素数。 程序分析:判断素数的方法:用一个数分别去除2到sqrt(这个数),如果能被整除, 则表明此数不是素数,反之是素数。
package C; public class Sushu { public static void main(String[] args) { int sum=0; for (int i = 101 ...
- 从Unity学UE(一)之蓝图类的使用----制作一个可控灯光
转自:http://blog.csdn.net/u011707076/article/details/44171829 首先申明,本文章内容适合有Unity引擎基础的童鞋享用,如果不了解Unity引擎 ...
- System.MissingMethodException: 找不到方法:
This is a problem which can occur when there is an old version of a DLL still lingering somewhere ar ...
- find command in linux terminal
Say you want to search for files with keyword as "keyword". find / -name keyword 2> /de ...
- Android中的文件权限操作
默认本工程创建的文件本工程对其有读写权限. 我们可以通过context.openFileOutput("文件名", 模式): 我们可以创建私有, 共有, 只读, 只写文件, 默认的 ...
- Javascript 简单学习
一直就崇拜JS如此牛叉的来操作Html标签, 抽出时间来学习学习,哈哈. Js是Netscape公司语言, 基于对象和事件驱动. 与Java不同之处: 1:所属公司不同 2:js是基于对象,java是 ...