_stdcall,_cdecl区别

(1) _stdcall调用
  _stdcall是Pascal程序的缺省调用方式,参数采用从右到左的压栈方式,被调函数自身在返回前清空堆栈。
  WIN32 Api都采用_stdcall调用方式,这样的宏定义说明了问题:
  #define WINAPI _stdcall

  按C编译方式,_stdcall调用约定在输出函数名前面加下划线,后面加"@"符号和参数的字节数,形如
_[email protected]。

?

(2) _cdecl调用
  _cdecl是C/C++的缺省调用方式,参数采用从右到左的压栈方式,传送参数的内存栈由调用者维护。_cedcl约定的函数只能被C/C++调用,每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。

  由于_cdecl调用方式的参数内存栈由调用者维护,所以变长参数的函数能(也只能)使用这种调用约定。关于C/C++中变长参数(…)的问题,笔者将另文详述。

  由于Visual C++默认采用_cdecl 调用方式,所以VC中中调用DLL时,用户应使用_stdcall调用约定。
  按C编译方式,_cdecl调用约定仅在输出函数名前面加下划线,形如_functionname。

?

?

几乎我们写的每一个WINDOWS API函数都是__stdcall类型的,为什么??
???? 首先,我们谈一下两者之间的区别:
?????? WINDOWS的函数调用时需要用到栈(STACK,一种先入后出的存储结构)。当函数调用完成后,栈需要清除,这里就是问题的关键,如何清除??
?????? 如果我们的函数使用了_cdecl,那么栈的清除工作是由调用者,用COM的术语来讲就是客户来完成的。这样带来了一个棘手的问题,不同的编译器产生栈的方式不尽相同,那么调用者能否正常的完成清除工作呢?答案是不能。
?????? 如果使用__stdcall,上面的问题就解决了,函数自己解决清除工作。所以,在跨(开发)平台的调用中,我们都使用__stdcall(虽然有时是以WINAPI的样子出现)。
??????
那么为什么还需要_cdecl呢?当我们遇到这样的函数如fprintf()它的参数是可变的,不定长的,被调用者事先无法知道参数的长度,事后的清除工作也无法正常的进行,因此,这种情况我们只能使用_cdecl。
?????? 到这里我们有一个结论,如果你的程序中没有涉及可变参数,最好使用__stdcal关键字

?

参考:http://www.cppblog.com/kenny/archive/2011/04/19/144539.html

时间: 2024-10-19 23:59:15

_stdcall,_cdecl区别的相关文章

调用约定_stdcall _cdecl _fastcall的区别

1.函数调用约定 函数的调用约定,顾名思义就是对函数调用的一个约束和规定(规范),描述了函数参数是怎么传递和由谁清除堆栈的.它决定以下内容: (1) 函数参数的压栈顺序: (2) 由调用者还是被调用者把参数弹出栈: (3) 产生函数修饰名的方法: 在看C++ primer中就提到函数声明包括:返回值类型,函数名,形参列表 int function(); int add(int a,int b); 上面的函数声明方式是我们经常用到的,其实还有一部分就是调用约定,目前存在:_cdecl._stdca

_stdcall ,_cdecl,__fastcall 深入解析

成相对独立的功能,它们彼此协作来完成整个软件系统的工作.可能存在一些模块的功能较为通用,在构造其它软件系统时仍会被使用.在构造软件系统时,如果将所有模块的源代码都静态编译到整个应用程序 EXE 文件中,会产生一些问题:一个缺点是增加了应用程序的大小,它会占用更多的磁盘空间,程序运行时也会消耗较大的内存空间,造成系统资源的浪费:另一个缺点是,在编写大的 EXE 程序时,在每次修改重建时都必须调整编译所有源代码,增加了编译过程的复杂性,也不利于阶段性的单元测试. Windows 系统平台上提供了一种

转:函数调用的区别:_cdecl以及_stdcall

函数调用的几个概念:_stdcall,_cdecl.... 1._stdcall是Pascal程序的缺省调用方式,通常用于Win32 Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈.VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数. 2.C调用约定(即用__cdecl关键字说明)按从右至左的顺序压参数入栈,由调用者把参数弹出栈.对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数的函数只能使用该调用约定).另外,在函数名修饰约定方

Linux Kernel - Debug Guide (Linux内核调试指南 )

http://blog.csdn.net/blizmax6/article/details/6747601 linux内核调试指南 一些前言 作者前言 知识从哪里来 为什么撰写本文档 为什么需要汇编级调试 ***第一部分:基础知识*** 总纲:内核世界的陷阱 源码阅读的陷阱 代码调试的陷阱 原理理解的陷阱 建立调试环境 发行版的选择和安装 安装交叉编译工具 bin工具集的使用 qemu的使用 initrd.img的原理与制作 x86虚拟调试环境的建立 arm虚拟调试环境的建立 arm开发板调试环

linux内核调试指南

linux内核调试指南 一些前言 作者前言 知识从哪里来 为什么撰写本文档 为什么需要汇编级调试 ***第一部分:基础知识*** 总纲:内核世界的陷阱 源码阅读的陷阱 代码调试的陷阱 原理理解的陷阱 建立调试环境 发行版的选择和安装 安装交叉编译工具 bin工具集的使用 qemu的使用 initrd.img的原理与制作 x86虚拟调试环境的建立 arm虚拟调试环境的建立 arm开发板调试环境的建立 gdb基础 基本命令 gdb之gui gdb技巧 gdb宏 汇编基础--X86篇 用户手册 AT&

调用 Dll 中的函数时,出现栈(STACK)的清除问题 -> 故障模块名称: StackHash_0a9e

在一个名为 test.dll 文件中,有一个 Max() 函数的定义是: #ifdef BUILD_DLL #define DLL_EXPORT __declspec(dllexport) __stdcall #else #define DLL_EXPORT __declspec(dllimport) __stdcall #endif int DLL_EXPORT Max(int x, int y); 当我在c程序中,定了一个函数指针类型为: int (*func)(int, int) 时 HM

C语言-第35课 - 函数调用行为

第35课 - 函数调用行为 活动记录 活动记录是函数调用时用于记录一系列相关信息的记录,包括: l 临时变量域:用来存放临时变量的值,如k++的中间结果(生成临时变量,将k的值赋值给临时变量,k=k+1)--temp. l 局部变量域:用来存放函数本次执行中的局部变量. l 机械状态域:用来保存调用函数之前有关机器状态的信息,包括各种寄存器的当前值和返回值等. l 实参数域:用于存放函数的实参信息. 参数入栈 既然函数参数的计算次序是依赖编译器实现的,那么函数参数的入栈次序是如何确定的呢?由调用

_cdecl 与 _stdcall 区别

前段时间编程时遇到过这么一个问题,我写了一个DLL,把里面的一个函数导出来,然后再定义一个签名与其匹配的函数指针,动态地把这个DLL加载起来(LoadLibrary),得到函数指针后,一调用,结果报错了,错误如下: Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function dec

Misc: 那些放在函数定义前的 _cdecl , _stdcall, _fastcall 到底是什么鬼

如果看一些很底层的代码(比如很多库的代码,或者内核代码),会经常见到形如 _cdecl  ,  _stdcall (这个在Windows下的代码中很常见),  _fastcall (这个比较少见)这一类的东西,到底是什么? 这些其实是用来指示编译器做函数调用的时候,应该如何调用,比如说, a)参数的传递是放在寄存器中还是放在栈中, b)参数是从左传到右还是从右传到左, etc. 说得专业一点,就是calling convention. 这些关乎系统底层ABI层面的东西.处理得不好,你的程序在链接