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代码的程序员我想不多,因为这方面的资料太少了,另一方面,它还需要有基本的汇编语言知识,关於汇编语言的 ...
随机推荐
- mysql分库分表
1.分库分表 很明显,一个主表(也就是很重要的表,例如用户表)无限制的增长势必严重影响性能,分库与分表是一个很不错的解决途径,也就是性能优化途径,现在的案例是我们有一个1000多万条记录的用户表mem ...
- 【leetcode❤python】 58. Length of Last Word
#-*- coding: UTF-8 -*-#利用strip函数去掉字符串去除空格(其实是去除两边[左边和右边]空格)#利用split分离字符串成列表class Solution(object): ...
- 浅思OC的语言特性
算了算,学习IOS已经有一段时间了.今天花了点时间思考一下OC的语言特性,让自己的心不要那么浮躁,注重基础,回归本源. OC做为一门面向对象语言,自然具有面向对象的语言特性,如封装.继承.多态.他具有 ...
- MySQL(七) —— MySQL存储过程 & 存储引擎
MySQL中输入语句的执行过程: 如果我们可以将上面的过程简化,吧语法分析或者编译等步骤简化,则可以将整个流程简化. 存储过程: 是SQL语句和控制语句的预编译集合,以一个名称存储并作为一个单元处理: ...
- PythonOCC 3D图形库学习—导入STEP模型
PythonOCC comes with importers/exporters for the most commonly used standard data files format in en ...
- BP神经网络求解异或问题(Python实现)
反向传播算法(Back Propagation)分二步进行,即正向传播和反向传播.这两个过程简述如下: 1.正向传播 输入的样本从输入层经过隐单元一层一层进行处理,传向输出层:在逐层处理的过程中.在输 ...
- 502 Proxy Error The proxy server received an invalid response from an upstream server
Proxy Error The proxy server received an invalid response from an upstream server. The proxy server ...
- ARM处理器启动流程
根据<<芯片手册>>查看相关内容: 1.启动方式 2.地址布局 3.启动流程
- Deep Learning: Activation Function
Sigmoid Function ReLU Function Tanh Function
- CSS笔记(七)列表
CSS 列表属性允许你放置.改变列表项标志,或者将图像作为列表项标志. 参考:http://www.w3school.com.cn/css/css_list.asp 实例: <html> ...