函数调用的区别:_cdecl以及_stdcall
一、概念
1)_stdcall调用
_stdcall是Pascal程序的缺省调用方式,参数采用从右到左的压栈方式,由调用者完成压栈操作,被调函数自身在返回前清空堆栈。
WIN32 Api都采用_stdcall调用方式,这样的宏定义说明了问题: #define WINAPI _stdcall
按C编译方式,_stdcall调用约定在输出函数名前面加下划线,后面加“@”符号和参数的字节数,形如_functionname@number.
Therefore, the function declared as int func( int a, double b )
is decorated as follows: _func@12
.
按C++编译方式,可参看(三)
2)_cdecl调用
_cdecl是C/C++的缺省调用方式,参数采用从右到左的压栈方式,由调用者完成压栈操作
,传送参数的内存栈由调用者维护。
_cedcl约定的函数只能被C/C++调用,每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。
按C编译方式,_cdecl调用约定仅在输出函数名前面加下划线,形如_functionname。
按C++编译方式,可参看(三)
二、区别
几乎我们写的每一个WINDOWS API函数都是__stdcall类型的,首先,需要了解两者之间的区别:
WINDOWS的函数调用时需要用到栈(STACK,一种先入后出的存储结构)。当函数调用完成后,栈需要清除,这里就是问题的关键,如何清除?如果我
们的函数使用了_cdecl,那么栈的清除工作是由调用者,用COM的术语来讲就是客户来完成的。这样带来了一个棘手的问题,不同的编译器产生栈的方式不
尽相同,那么调用者能否正常的完成清除工作呢?答案是不能。如果使用__stdcall,上面的问题就解决了,函数自己解决清除工作。所以,在跨(开发)
平台的调用中,我们都使用__stdcall(虽然有时是以WINAPI的样子出现)。那么为什么还需要_cdecl呢?当我们遇到这样的函数如
fprintf()它的参数是可变的,不定长的,被调用者事先无法知道参数的长度,事后的清除工作也无法正常的进行,因此,这种情况我们只能使用
_cdecl。到这里我们有一个结论,如果你的程序中没有涉及可变参数,最好使用__stdcall关键字。
三、编译时函数名修饰约定规则
1)
首先介绍一下函数修饰名,C”
或者“C++”函数在内部(编译和链接)通过修饰名识别。修饰名是编译器在编译函数定义或者原型时生成的字符串。有些情况下使用函数的修饰名是必要的,如
在模块定义文件里头指定输出“C++”重载函数、构造函数、析构函数,又如在汇编代码里调用“C””或“C++”函数等。修饰名由函数名、类名、调用约
定、返回类型、参数等共同决定。名字修饰约定随调用约定和编译种类(C或C++)的不同而变化。函数名修饰约定随编译种类和调用约定的不同而不同。下面详
细说明在C++编译器中修饰名的约定方法,在C编译器中的变化已经在(一)中阐述。
2)C++编译时函数名修饰约定规则
__stdcall调用约定:
1)、以"?"标识函数名的开始,后跟函数名;
2)、函数名后面以"@@YG"标识参数表的开始,后跟参数表;
3)、参数表以代号表示:
X--void ,
D--char,
E--unsigned char,
F--short,
H--int,
I--unsigned int,
J--long,
K--unsigned long,
M--float,
N--double,
_N--bool,
PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以"0"代替,一个"0"代表一次重复;
4)、参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前;
5)、参数表后以"@Z"标识整个名字的结束,如果该函数无参数,则以"Z"标识结束。
其格式为"?functionname@@YG*****@Z"或"?functionname@@YG*XZ",例如 int Test1(char *var1,unsigned long)-----“?Test1@@YGHPADK@Z” void Test2() -----“?Test2@@YGXXZ”
__cdecl调用约定:
规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YA"。
四、在C++编译时,引入c库里面的函数的处理
如果Add(int a, int
b)是在c语言编译器编译,而在c++文件使用,由(三)知,因为c编译器和c++编译器对函数名的解释不一样(c++编译器解释函数名的时候要考虑函数
参数,这样是了方便函数重载,而在c语言中不存在函数重载的问题),会出现链接错误。 哪么该如何处理呢?这是关键字extern就派上用场了。
而在c++文件使用c编译器编译过的函数,则需要在c++文件中声明:extern "C" Add(int a, int b),使用extern "C",实质就是告诉c++编译器,该函数是c库里面的函数。请保持我的名称,不要给我生成用于链接的函数修饰名。
另外顺便说一下extern的另一个用途,extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在
其他模块中寻找其定义。#inlcude "***.***" 和 int f() 等价于 extern int f() ;即
extern 申明的函数前不需要加引用头文件就能用。
参考文献:
博客:http://www.cnblogs.com/Winston/archive/2008/09/11/1289391.html
函数调用的区别:_cdecl以及_stdcall的更多相关文章
- 转:函数调用的区别:_cdecl以及_stdcall
函数调用的几个概念:_stdcall,_cdecl.... 1._stdcall是Pascal程序的缺省调用方式,通常用于Win32 Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈.VC将 ...
- _cdecl与_stdcall区别
_cdecl与_stdcall是最常用的的两种函数调用修饰,区别在于函数返回时,清理栈(恢复栈平衡)是caller做还是被调函数做. : _cdecl int add1(int a, int b) : ...
- C++编译器详解(三)函数调用的区别:_cdecl以及_stdcall
1._stdcall是Pascal程序的缺省调用方式,通常用于Win32 API中,函数采用从右到左的压栈方式,自己在退出时清空堆栈.VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上&qu ...
- _cdecl 与 _stdcall 区别
前段时间编程时遇到过这么一个问题,我写了一个DLL,把里面的一个函数导出来,然后再定义一个签名与其匹配的函数指针,动态地把这个DLL加载起来(LoadLibrary),得到函数指针后,一调用,结果报错 ...
- 函数调用方法之__cdecl与_stdcall
在debug VS c工程文件时,碰到cannot convert from 'int (__cdecl *)(char *)' to 'xxx'这个问题,发现问题在于typedef函数指针类型时,将 ...
- python中方法调用和函数调用的区别
函数调用: 传几个参数,就会有几个实参方法调用: 默认传递一个参数self,至少要定义一个形参
- C++ 普通函数和虚函数调用的区别
引出:写个类A,声明类A指针指向NULL,调用类A的方法会有什么后果,编译通过吗,运行会通过吗? #include<stdio.h> #include<iostream> us ...
- 第05课:GDB常用命令详解(中)
本科核心内容: info和thread命令 next.step.util.finish和return命令 5.1info和thread命令 在前面使用info break命令查看当前断点时介绍过,in ...
- 第06课:GDB 常用命令详解(中)
本课的核心内容: info 和 thread 命令 next.step.util.finish.return 和 jump 命令 info 和 thread 命令 在前面使用 info break 命 ...
随机推荐
- IP、TCP、DNS协议
·······················································IP协议························· 位于网络层,作用是把数据包传送 ...
- (O)WEB:前端网站性能优化(原创)
*从理论.实战编码.实战调试3个方面学习前端性能优化(包括页面加载时间和页面流畅度): -------------------------------理论----------------------- ...
- 如何通过RNA-Seq了解转录本的结构
[转载]如何通过RNA-Seq了解转录本的结构 已有 1942 次阅读 2014-12-26 15:22 |个人分类:转录组测序|系统分类:科研笔记|关键词:RNA-Seq,转录组测序,转录本结构| ...
- SVN忘记登陆用户
C:\Users\Yaolz\AppData\Roaming\Subversion\auth 删除里面所有文件
- nagios 报警参数
host_notification_options: d = notify on DOWN host states, u = notify on UNREACHABLE host states r = ...
- 基于akka-http搭建restfull框架
1.scala开发环境介绍 2.scala插件的demo模板介绍 3.akka-http提供demo研究 4.添加路由机制解析 package org.netsharp.rest import akk ...
- jenkins可选插件为空的解决方式
我是安装的jenkins,文件名字是这个jenkins.msi,点finish安装完成,然后浏览器就自动打开了jenkins页面,输入密码后,便进入了如下页面 之后我是选择的跳过插件安装,在可选插件里 ...
- tomcat 配置图片服务器
在后台和前端交互时,遇到了后台存储的图片,前端根据地址无法访问,使用Tomcat搭建图片服务器 1.找到tomcat下的server.xml文件 2.配置文件下加入service节点(在原有的serv ...
- 轮播图js编写
//面向对象 function Left() { this.index = 0; this.lefthover = $('#left-content'); this.listenhover(); th ...
- 迁移桌面程序到MS Store(5)——.NET Standard
接下来的几篇,我想讨论下迁移桌面程序到MS Store,可以采用的比较常见.通用性比较强的实施步骤和分层架构. 通常商业项目一般都是不断的迭代,不太可能突然停止更新现有的桌面版本,然后花很长时间从头来 ...