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的区别的更多相关文章

  1. stdcall, cdecl, pascal 区别(转载)

    转载自:http://www.cnblogs.com/lidabo/archive/2012/11/21/2781484.html stdcall, cdecl, pascal 区别 这三个参数都是告 ...

  2. stdcall、cdecl详解(以及WINAPI和CALLBACK之类的宏对应什么)

    转自:http://blog.csdn.net/huanjieshuijing/article/details/5822942 对_stdcall 的理解在C语言中,假设我们有这样的一个函数:int ...

  3. stdcall、cdecl、fastcall、thiscall 、naked call的汇编详解

    函数调用规范   当高级语言函数被编译成机器码时,有一个问题就必须解决:因为CPU没有办法知道一个函数调用需要多少个.什么样的参数.即计算机不知道怎么给这个函数传递参数,传递参数的工作必须由函数调用者 ...

  4. Java调用动态库方法说明-最详细

    Java不能直接调用由c或者c++写得dll(TF_ID.dll),所以只能采用jni得方法,一步一步生成符合规范得dll文件(假设叫FANGJIAN.dll),在FANGJIAN.dll这个文件里来 ...

  5. c++中的几种函数调用约定(转)

    C++中的函数调用约定(调用惯例)主要针对三个问题: 1.参数传递的方式(是否采用寄存器传递参数.采用哪个寄存器传递参数.参数压桟的顺序等): 参数的传递方式,最常见的是通过栈传递.函数的调用方将参数 ...

  6. Delphi中register, pascal, cdecl, stdcall, safecall(转)

    源:http://blog.sina.com.cn/s/blog_552c78120100hsr9.html 注: 使用错误,或者在该加的地方没有加,可能会出现"privileged ins ...

  7. Object Pascal 过程与函数

    过程与函数 过程与函数是实现一定功能的语句块,是程序中的特定功能单元.可以在程序的其他地方被调用,也可以进行递归调用.过程与函数的区别在于过程没有返回值,而函数有返回值. 1.过程与函数的定义 过程与 ...

  8. 【转】Delphi内嵌ASM简易教程

    Delphi内嵌ASM简易教程 作者:heiying2006-03-19 18:33分类:默认分类标签: 前言 Delphi作为一个快速高效的开发平台,使用的人越来越多,但熟悉在Delphi代码中嵌入 ...

  9. Delphi代码中嵌入ASM代码

    前言 Delphi作为一个快速高效的开发平台,使用的人越来越多,但熟悉在Delphi代码中嵌入ASM代码的程序员我想不多,因为这方面的资料太少了,另一方面,它还需要有基本的汇编语言知识,关於汇编语言的 ...

随机推荐

  1. uploadify的用法与动态传参 提供demo下载

    ---恢复内容开始--- 官网:http://www.uploadify.com/   一款不错的上传插件.官方文档http://www.uploadify.com/documentation/ 用法 ...

  2. 2016年11月5日 星期六 --出埃及记 Exodus 19:21

    2016年11月5日 星期六 --出埃及记 Exodus 19:21 and the LORD said to him, "Go down and warn the people so th ...

  3. shell脚本例子集锦(习题总结)

    练习一:写一个脚本 .设定变量FILE的值为/etc/passwd .依次向/etc/passwd中的每个用户问好,并且说出对方的ID是什么 形如:(提示:LINE=`wc -l /etc/passw ...

  4. ASP.NET简单登录注册实例

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx. ...

  5. css参考文档; 官方英文说明!! 1 margin padding 百分比参照物 2 margin值为auto时的说明 3 div在div里垂直居中方法 4 dispaly:flex说明

    css参考文档        http://css.doyoe.com/ 两篇很好的文章:(下面的css官方英文说明链接 有时间可以研究下 http://www.w3.org/TR/css3-box/ ...

  6. include_path详细解析

    include_path详细解析     原文地址:http://www.laruence.com/2010/05/04/1450.html 1.php默认的包含路径为 .;C:\php\pear 即 ...

  7. Word和Windows有严重的bug这样下去微软堪忧

    Word和Windows对微软的重要性就像C语言的指针. Windows中特别常用的搜索功能有严重的bug,常常搜不到Excel文件. Word中的排版功能有严重的bug,有图超过几十页就无法排版了, ...

  8. C#四则运算之策略模式

    Calculator.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; ...

  9. install Matlab2016b on Ubuntu 14.04

    From Download Download the install file from Download MATLAB, Simulink, Stateflow, and Other MathWor ...

  10. Installing Python 3.5.2 from source

    Here are the procedures we are to follow, Download the source code of an official Python release. Co ...