Posted on Tuesday, May 24, 2005 3:23 PM #C & C++
<!--
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
<rdf:Description
rdf:about="http://blog.bcchinese.net/happyjet/archive/2005/05/24/22545.aspx"
dc:identifier="http://blog.bcchinese.net/happyjet/archive/2005/05/24/22545.aspx"
dc:title="函数调用规范__cdecl和__stdcall的区别一目了然(表格形式)"
trackback:ping="http://blog.bcchinese.net/happyjet/services/trackbacks/22545.aspx" />
</rdf:RDF>
-->
__cdecl
|
__stdcall
|
C
和
C++
程序的缺省调用规范
|
为了使用这种调用规范,需要你明确的加上
__stdcall
(或
WINAPI
)文字。即
return-type
__stdcall
function-name
[(argument-list
)]
|
在被
调用函数
(Callee)
返回后
,由调用方
(Caller)
调整堆栈。
1.
调用方的函数调用
2.
被调用函数的执行
3.
被调用函数的结果返回
4.
调用方清除调整堆栈
|
在被
调用函数
(Callee)
返回前
,由被
调用函数
(Callee)
调整堆栈。图示:
1.
调用方的函数调用
2.
被调用函数的执行
3.
被调用函数清除调整堆栈
4.
被调用函数的结果返回
|
因为每个调用的地方都需要生成一段调整堆栈的代码,所以最后生成的文件较大。
|
因为调整堆栈的代码只存在在一个地方(被调用函数的代码内),所以最后生成的文件较小。
|
函数的参数个数可变(就像
printf
函数一样),因为只有调用者才知道它传给被调用函数几个参数,才能在调用结束时适当地调整堆栈。
|
函数的参数个数不能是可变的。
|
对于定义在
C
程序文件中的输出函数,函数名会保持原样,不会被修饰。
对于定义在
C++
程序文件中的输出函数,函数名会被修饰,
MSDN
说
Underscore character (_) is prefixed to names
.
我实际测试(
VC4
和
VC6
)下来发现好像不是那么简单。
可通过在前面加上
extern
“C
”
以去除函数名修饰。也可通过
.def
文件去除函数名修饰。
|
不论是
C
程序文件中的输出函数还是
C++
程序文件中的输出函数,函数名都会被修饰。
对于定义在
C
程序文件中的输出函数,
An
underscore (_) is prefixed to the name. The name is followed by the at
sign (@) followed by the number of bytes (in decimal) in the argument
list.
对于定义在
C++
程序文件中的输出函数,好像更复杂,和
__cdecl
的情况类似。
好像只能通过
.def
文件去除函数名修饰。
|
_beginthread
需要
__cdecl
的线程函数地址
|
_beginthreadex
和
CreateThread
需要
__stdcall
的线程函数地址
|
两者的参数传递顺序都是从右向左。
为了让
VB
可以调用,需要用
__stdcall
调用规范来定义
C/C++
函数。请参看Microsoft KB153586
文章:How To Call C Functions That Use the _cdecl Calling Convention
。
当你
LoadLibrary
一个
DLL
文件后, 把
GetProcAddress
取得的函数地址传给上面三个线程生成函数时,请务必确认实际定义在
DLL
文件的输出函数符合调用规范要求。否则,编译成
Release
版后运行,可能会破坏堆栈,程序行为不可预测。
VC
中的相关编译开关:/Gd /Gr /Gz
。另外,VC6
中新增加的 /GZ
编译开关可以帮你检查堆栈问题。
我也是初学者,若有不对的地方、可以补充的地方,请指教。谢谢。
|
(补充)汇编语言视点的比较文章:
Intel x86 Function-call Conventions - Assembly View
调用约定
调用约定(Calling convention)决定以下内容:函数参数的压栈顺序,由调用者还是被调用者把参数弹出栈,以及产生函数修饰名的方法。MFC支持以下调用约定:
_cdecl
按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于“C”函数或者变量,修饰名是在函数名前加下划线。对于“C++”函数,有所不同。
如函数void test(void)的修饰名是_test;对于不属于一个类的“C++”全局函数,修饰名是?test@@ZAXXZ。
这是MFC缺省调用约定。由于是调用者负责把参数弹出栈,所以可以给函数定义个数不定的参数,如printf函数。
_stdcall
按从右至左的顺序压参数入栈,由被调用者把参数弹出栈。对于“C”函数或者变量,修饰名以下划线为前缀,然后是函数名,然后是符号“@”及参数的字节数,
如函数int func(int a, double b)的修饰名是_func@12。对于“C++”函数,则有所不同。
所有的Win32 API函数都遵循该约定。
_fastcall
头两个DWORD类型或者占更少字节的参数被放入ECX和EDX寄存器,其他剩下的参数按从右到左的顺序压入栈。由被调用者把参数弹出栈,对于“C”函数
或者变量,修饰名以“@”为前缀,然后是函数名,接着是符号“@”及参数的字节数,如函数int func(int a, double
b)的修饰名是@func@12。对于“C++”函数,有所不同。
未来的编译器可能使用不同的寄存器来存放参数。
thiscall
仅仅应用于“C++”成员函数。this指针存放于CX寄存器,参数从右到左压栈。thiscall不是关键词,因此不能被程序员指定。
naked call
采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked call不产生这样的代码。
naked call不是类型修饰符,故必须和_declspec共同使用,如下:
__declspec( naked ) int func( formal_parameters )
{
// Function body
}
过时的调用约定
原来的一些调用约定可以不再使用。它们被定义成调用约定_stdcall或者_cdecl。例如:
#define CALLBACK __stdcall
#define WINAPI __stdcall
#define WINAPIV __cdecl
#define APIENTRY WINAPI
#define APIPRIVATE __stdcall
#define PASCAL __stdcall
分享到:
相关推荐
函数的调用规则(__cdecl,__stdcall,__fastcall,__pascal) 关于函数的调用规则(调用约定),大多数时候是不需要了解的,但是如果需要跨语言的编程,比如VC写的dll要delphi调用,则需要了解。 microsoft的vc默认的是...
关于函数调用方式__stdcall和__cdecl详解 __stdcall __cdecl 两者的相同点与不同点 实例 __stdcall __stdcall的全称是standard call。是C++的标准调用方式。 函数参数的入栈顺序为从右到左入栈。函数返回时使用retn ...
Visual C/C++的编译器提供了几种函数调用约定,了解这些函数调用约定的含义及它们...__stdcall 被调用函数 右à 左 _函数名@数字 __fastcall 被调用函数 右à 左 @函数名@数字 thiscall(非关键字) 被调用函数 右à 左 /
_stdcall、_cdecl和_fastcall 的区别.zip
解决error LNK2001 无法解析的外部符号 int __cdecl sprintf
易语言cdecl回调处理源码,cdecl回调处理,stdcall_to_cdecl,stdcall_to_cdecl_free,回调函数,test,VirtualAlloc,VirtualFree,set_data
解决error LNK2005 void __cdecl operator delete(void
__stdcall_stdcall 是StandardCall的缩写,是C++的标准调用方式:所有参数从右到左依次入栈,由调用者负责把参数压入栈,最后由被调用者负责清除栈的内容,Windows API 所采用的函数调用规则则是这种规则 ...
cdecl函数调用,了解printf这样的函数调用,对比stdcall会更清楚.zip
_cdecl、_stdcall、_fastcall和_thiscall整理
windows系统调用函数的方法有3种:__stdcall , __cdecl ,PASCAL 前两种是从右向左传递参数,最后一种是从左向右传递参数. __stdcall是windows系统调用API的标准方式 __cdecl是ANSI-C的标准调用方式
包含 Java 通过 RXTXComm 读写串口数据需要的动态链接库:SuperComSCL2008.Dll、rxtxSerial.dll、rxtxParallel.dll 和操作 SCL2008 显示屏的动态链接库 SCL_API_stdcall.dll、SCL_API_cdecl.dll
vb6.0 __stdcall函数内调用__cdecl函数
说明了 stdcall 与 cdecl 的区别
C++调用C函数实例详解 前言:以前见到extern “C”这样的语句,只是简单地知道跟外部链接有关,但是没有深刻理解它的意思。 首先,为什么要使用extern “C”修饰符? C++调用其它语言的函数,由于编译器生成函数的...
1>save-image-D435.obj : error LNK2001: 无法解析的外部符号 “private: char * __cdecl cv::String::allocate(unsigned __int64)” (?allocate@String@cv@@AEAAPEAD_K@Z) 1>save-image-D435.obj : error LNK2001: ...
stdcall cdecl 函数调用方式详解