http://blog.sina.com.cn/s/blog_5fa3bba00100jkaa.html

最大的区别就是__cdecl是由调用函数者释放堆栈,而__stdcall由被调用者释放。但是我们在调用任一种函数时,都没有进行显示的释放堆栈工作。这个工作由编译器完成,它帮我们生成了这个中间要使用的代码。

这两种调用参数入栈方式一样, 都是先函数参数入栈,再是函数返回地址入栈,再是函数局部变量入栈。 这两种区别只是退栈时,谁来清除栈,这点不一样。 如下例子,是被调函数本身自己清除(比如stdcall那个汇编语句 ret 8),还是调用方清除(比如cdecl的add esp, 8)这点不一样。

以下系转载:

在C语言中,假设我们有这样的一个函数:
  
  int function(int a,int b)
  
  调用时只要用result = function(1,2)这样的方式就可以使用这个函数。但是,当高级语言被编译成计算机可以识别的机器码时,有一个问题就凸现出来:在CPU中,计算机没有办法知道一个函数调用需要多少个、什么样的参数,也没有硬件可以保存这些参数。也就是说,计算机不知道怎么给这个函数传递参数,传递参数的工作必须由函数调用者和函数本身来协调。为此,计算机提供了一种被称为栈的数据结构来支持参数传递。

栈是一种先进后出的数据结构,栈有一个存储区、一个栈顶指针。栈顶指针指向堆栈中第一个可用的数据项(被称为栈顶)。用户可以在栈顶上方向栈中加入数据,这个操作被称为压栈(Push),压栈以后,栈顶自动变成新加入数据项的位置,栈顶指针也随之修改。用户也可以从堆栈中取走栈顶,称为弹出栈(pop),弹出栈后,栈顶下的一个元素变成栈顶,栈顶指针随之修改。
函数调用时,调用者依次把参数压栈,然后调用函数,函数被调用以后,在堆栈中取得数据,并进行计算。函数计算结束以后,或者调用者、或者函数本身修改堆栈,使堆栈恢复原装。
在参数传递中,有两个很重要的问题必须得到明确说明:
  
  当参数个数多于一个时,按照什么顺序把参数压入堆栈
  函数调用后,由谁来把堆栈恢复原状
  在高级语言中,通过函数调用约定来说明这两个问题。常见的调用约定有:
stdcall
  cdecl
  fastcall
  thiscall
  naked call

1. stdcall调用约定
  stdcall很多时候被称为pascal调用约定,因为pascal是早期很常见的一种教学用计算机程序设计语言,其语法严谨,使用的函数调用约定就是stdcall。在Microsoft C++系列的C/C++编译器中,常常用PASCAL宏来声明这个调用约定,类似的宏还有WINAPI和CALLBACK。
stdcall调用约定声明的语法为(以前文的那个函数为例):
  
  int __stdcall function(int a,int b)
  
  stdcall的调用约定意味着:1)参数从右向左压入堆栈,2)函数自身修改堆栈 3)函数名自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸
以上述这个函数为例,参数b首先被压栈,然后是参数a,函数调用function(1,2)调用处翻译成汇编语言将变成:
push 2        第二个参数入栈
  push 1        第一个参数入栈
  call function    调用参数,注意此时自动把cs:eip入栈
而对于函数自身,则可以翻译为:
  push ebp       保存ebp寄存器,该寄存器将用来保存堆栈的栈顶指针,可以在函数退出时恢复
  mov ebp, esp    保存堆栈指针
  mov eax,[ebp + 8H] 堆栈中ebp指向位置之前依次保存有ebp, cs:eip, a, b, ebp +8指向a
  add eax,[ebp + 0CH] 堆栈中ebp + 12处保存了b
  mov esp, ebp    恢复esp
  pop ebp
  ret 8
而在编译时,这个函数的名字被翻译成_function@8
注意不同编译器会插入自己的汇编代码以提供编译的通用性,但是大体代码如此。其中在函数开始处保留esp到ebp中,在函数结束恢复是编译器常用的方法。
从函数调用看,2和1依次被push进堆栈,而在函数中又通过相对于ebp(即刚进函数时的堆栈指针)的偏移量存取参数。函数结束后,ret 8表示清理8个字节的堆栈,函数自己恢复了堆栈。

2.cdecl调用约定
  cdecl调用约定又称为C调用约定,是C语言缺省的调用约定,它的定义语法是:
int function (int a ,int b) //不加修饰就是C调用约定
  int __cdecl function(int a,int b)//明确指出C调用约定
在写本文时,出乎我的意料,发现cdecl调用约定的参数压栈顺序是和stdcall是一样的,参数首先由右向左压入堆栈。所不同的是,函数本身不清理堆栈,调用者负责清理堆栈。由于这种变化,C调用约定允许函数的参数的个数是不固定的,这也是C语言的一大特色。对于前面的function函数,使用cdecl后的汇编码变成:
调用处
  push 1                  (这里写反了吧,应该是push 2)
  push 2                  (这里写反了吧,应该是push 1)
  call function
  add esp, 8    注意:这里调用者在恢复堆栈
被调用函数_function处
  push ebp       保存ebp寄存器,该寄存器将用来保存堆栈的栈顶指针,可以在函数退出时恢复
  mov ebp,esp     保存堆栈指针
  mov eax,[ebp + 8H] 堆栈中ebp指向位置之前依次保存有ebp,cs:eip,a,b,ebp +8指向a
  add eax,[ebp + 0CH] 堆栈中ebp + 12处保存了b
  mov esp,ebp     恢复esp
  pop ebp
  ret        注意,这里没有修改堆栈
MSDN中说,该修饰自动在函数名前加前导的下划线,因此函数名在符号表中被记录为_function,但是我在编译时似乎没有看到这种变化。
由于参数按照从右向左顺序压栈,因此最开始的参数在最接近栈顶的位置,因此当采用不定个数参数时,第一个参数在栈中的位置肯定能知道,只要不定的参数个数能够根据第一个后者后续的明确的参数确定下来,就可以使用不定参数,例如对于CRT中的sprintf函数,定义为:
  int sprintf(char* buffer,const char* format,...)
  由于所有的不定参数都可以通过format确定,因此使用不定个数的参数是没有问题的。

转载于:https://www.cnblogs.com/viviancc/archive/2012/10/22/2733768.html

__cdecl __stdcall区别-转相关推荐

  1. __stdcall和__cdecl的区别

    __stdcall和__cdecl是两种函数名字修饰.(注意是连续的两个下划线) Windows上 windows上不管是C还是C++,默认使用的都是__stdcall方式. 不论__stdcall还 ...

  2. __stdcall 和 __cdecl 的区别浅析

    1. __cdecl __cdecl 是C Declaration的缩写(declaration,声明),表示C语言默认的函数调用方法:所有参数从右到左依次入栈,由调用者负责把参数压入栈,最后也是由调 ...

  3. 函数的调用规则(__cdecl,__stdcall,__fastcall,__pascal)

    关于函数的调用规则(调用约定),大多数时候是不需要了解的,但是如果需要跨语言的编程,比如VC写的dll要delphi调用,则需要了解. microsoft的vc默认的是__cdecl方式,而windo ...

  4. __cdecl __stdcall

    1.如果函数func是__cdecl(默认调用方式),调用时情况如下 int    main()      ... {     //参数从右到左压栈     push   4     push   3 ...

  5. 【转】调用约定__cdecl、__stdcall和__fastcall的区别

    什么是调用约定 函数的调用约定,顾名思义就是对函数调用的一个约束和规定(规范),描述了函数参数是怎么传递和由谁清除堆栈的.它决定以下内容:(1)函数参数的压栈顺序,(2)由调用者还是被调用者把参数弹出 ...

  6. 函数调用规范__cdecl和__stdcall的区别

    原作:葡萄架上的牵牛花 原地址:http://blog.bcchinese.net/happyjet/archive/2005/05/24/22545.aspx __cdecl __stdcall C ...

  7. __cdecl、__stdcall、__fastcall 与 __pascal 浅析

    X86调用约定 calling convention:https://www.cnblogs.com/shangdawei/p/3323252.html __cdecl.__stdcall.__fas ...

  8. OpenGL,GLUT,FreeGLUT,GLFW,GLEW,GLAD,GL3W,GLAD,GLM,GLSL的区别详解

    OpenGL,GLUT,FreeGLUT,GLFW,GLEW,GLAD,GL3W,GLAD,GLM,GLSL的区别详解 很多同学在初学计算机图形学时,都要去配置OpenGL环境,其中涉及多个库的选择和 ...

  9. 二十万字C/C++、嵌入式软开面试题全集宝典六

    目录 101. 字节对齐有什么作用? 102. C语言中#pragma用法 103. new和malloc的区别? 104. malloc/calloc/realloc三者之间的区别? 105. de ...

最新文章

  1. Python函数默认参数陷阱
  2. python并发之concurrent.futures
  3. 深度学习之卷积神经网络(2)卷积神经网络结构
  4. http-server 简介 复制的
  5. 修复ubuntu中其他盘不能挂载
  6. python列表去重_python 字典列表/列表套字典 去重重复的字典数据
  7. 微信小程序云开发教程-微信小程序的JSON配置
  8. 如何在30天内拿下软考初级程序员?
  9. linux进程调度时间片算法,进程调度算法–时间片轮转调度算法
  10. 3D变形:平移、旋转、缩放
  11. android 闪屏页 动画,记一次 Android 闪屏页适配
  12. SDPT3 4.0版——半正定二次线性规划的Matlab软件
  13. codewars练习(javascript)-2021/2/18
  14. amd显卡驱动linux 卸载,怎么彻底卸载AMD显卡驱动|完全卸载AMD显卡驱动步骤
  15. SQL 优化方案(实用版)
  16. SUS系统可用性量表
  17. jvm优化_在JVM中记录世界停顿
  18. OpenCV实战——拟合直线
  19. 百度api使用方法(java、js)
  20. The Django Book 第一章【Django介绍】

热门文章

  1. 致新手——OpenStack云倒底是什么?
  2. .CN域名总量达1090.6万个:8月份共净增13.8万个
  3. Memcached Java客户端2.6.1发布
  4. 关于hive中Map join 时大表left join小表的问题
  5. 2019.7.9刷题统计
  6. 总结day11 ----函数的学习(2)
  7. 【*2000】【2018-2019 ICPC, NEERC, Southern Subregional Contest C 】Cloud Computing
  8. 无法找到python第三方库的处理方法
  9. Bootstrap入门(三十)JS插件7:警告框
  10. ajaxfileupload 跨域 (二级域名) 可行办法