﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-程序人生-文章分类-NoteBook</title><link>http://www.cppblog.com/Cyclopedia/category/8944.html</link><description>技术点亮生活</description><language>zh-cn</language><lastBuildDate>Wed, 26 Nov 2008 18:47:59 GMT</lastBuildDate><pubDate>Wed, 26 Nov 2008 18:47:59 GMT</pubDate><ttl>60</ttl><item><title>__stdcall，__cdecl和__fastcall的作用 </title><link>http://www.cppblog.com/Cyclopedia/articles/67953.html</link><dc:creator>Cyclopedia</dc:creator><author>Cyclopedia</author><pubDate>Wed, 26 Nov 2008 16:18:00 GMT</pubDate><guid>http://www.cppblog.com/Cyclopedia/articles/67953.html</guid><wfw:comment>http://www.cppblog.com/Cyclopedia/comments/67953.html</wfw:comment><comments>http://www.cppblog.com/Cyclopedia/articles/67953.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Cyclopedia/comments/commentRss/67953.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Cyclopedia/services/trackbacks/67953.html</trackback:ping><description><![CDATA[1)调用约定<br>被以下几个修饰关键字修饰的函数，其参数都是从右向左通过堆栈传递的(<font color=#0000ff>__fastcall</font>的前面部分由ecx,edx传)，函数调用在返回前要清理堆栈，但由调用者还是被调用者清理不一定。<br>1、<font color=#0000ff>__stdcall</font>是Pascal程序的缺省调用方式，通常用于Win32&nbsp;Api中，函数采用从右到左的压栈方式，自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀，在函数名后加上"@"和参数的字节数。&nbsp;<font color=#0000ff>int</font>&nbsp;f(<font color=#0000ff>void</font>&nbsp;*p)&nbsp;&nbsp;--&gt;&gt;&nbsp;&nbsp;&nbsp;_f@4(在外部汇编语言里可以用这个名字引用这个函数)<br>2、C调用约定（即用<font color=#0000ff>__cdecl</font>关键字说明）（The&nbsp;C&nbsp;<font color=#0000ff>default</font>&nbsp;calling&nbsp;convention）按从右至左的顺序压参数入栈，由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的（正因为如此，实现可变参数vararg的函数(如printf)只能使用该调用约定）。另外，在函数名修饰约定方面也有所不同。&nbsp;<font color=#0000ff>_cdecl</font>是C和C＋＋程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码，所以产生的可执行文件大小会比调用<font color=#0000ff>_stdcall</font>函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。是MFC缺省调用约定。&nbsp;<br>3、<font color=#0000ff>__fastcall</font>调用的主要特点就是快，因为它是通过寄存器来传送参数的（实际上，它用ECX和EDX传送前两个双字（DWORD）或更小的参数，剩下的参数仍旧自右向左压栈传送，被调用的函数在返回前清理传送参数的内存栈），在函数名修饰约定方面，它和前两者均不同。<font color=#0000ff>__fastcall</font>方式的函数采用寄存器传递参数，VC将函数编译后会在函数名前面加上"@"前缀，在函数名后加上"@"和参数的字节数。&nbsp;<br>4、thiscall仅仅应用于&#8220;C++&#8221;成员函数。<font color=#0000ff>this</font>指针存放于CX/ECX寄存器中，参数从右到左压。thiscall不是关键词，因此不能被程序员指定。&nbsp;<br>5、naked&nbsp;call。当采用1-4的调用约定时，如果必要的话，进入函数时编译器会产生代码来保存ESI，EDI，EBX，EBP寄存器，退出函数时则产生代码恢复这些寄存器的内容。<br>(这些代码称作&nbsp;prolog&nbsp;and&nbsp;epilog&nbsp;code,一般，ebp,esp的保存是必须的).<br>但是naked&nbsp;call不产生这样的代码。naked&nbsp;call不是类型修饰符，故必须和<font color=#0000ff>_declspec</font>共同使用。&nbsp;<br>关键字&nbsp;<font color=#0000ff>__stdcall</font>、<font color=#0000ff>__cdecl</font>和<font color=#0000ff>__fastcall</font>可以直接加在要输出的函数前。它们对应的命令行参数分别为/Gz、/Gd和/Gr。缺省状态为/Gd，即<font color=#0000ff>__cdecl</font>。&nbsp;<br>要完全模仿PASCAL调用约定首先必须使用<font color=#0000ff>__stdcall</font>调用约定，至于函数名修饰约定，可以通过其它方法模仿。还有一个值得一提的是WINAPI宏，Windows.h支持该宏，它可以将出函数翻译成适当的调用约定，在WIN32中，它被定义为<font color=#0000ff>__stdcall</font>。使用WINAPI宏可以创建自己的APIs。&nbsp;<br><br>2)名字修饰约定<br>1、修饰名(Decoration&nbsp;name)&nbsp;<br>&#8220;C&#8221;或者&#8220;C++&#8221;函数在内部（编译和链接）通过修饰名识别。修饰名是编译器在编译函数定义或者原型时生成的字符串。有些情况下使用函数的修饰名是必要的，如在模块定义文件里头指定输出&#8220;C++&#8221;重载函数、构造函数、析构函数，又如在汇编代码里调用&#8220;C&#8221;&#8221;或&#8220;C++&#8221;函数等。&nbsp;修饰名由函数名、类名、调用约定、返回类型、参数等共同决定。<br>2、名字修饰约定随调用约定和编译种类(C或C++)的不同而变化。函数名修饰约定随编译种类和调用约定的不同而不同，下面分别说明。&nbsp;<br>a、C编译时函数名修饰约定规则：&nbsp;<br><font color=#0000ff>__stdcall</font>调用约定在输出函数名前加上一个下划线前缀，后面加上一个&#8220;@&#8221;符号和其参数的字节数，格式为_functionname@number。&nbsp;<br><font color=#0000ff>__cdecl</font>调用约定仅在输出函数名前加上一个下划线前缀，格式为_functionname。&nbsp;<br><font color=#0000ff>__fastcall</font>调用约定在输出函数名前加上一个&#8220;@&#8221;符号，后面也是一个&#8220;@&#8221;符号和其参数的字节数，<br>格式为@functionname@number。&nbsp;<br>它们均不改变输出函数名中的字符大小写，这和PASCAL调用约定不同，PASCAL约定输出的函数名无任何修饰且全部大写。&nbsp;<br>b、C++编译时函数名修饰约定规则：&nbsp;<br><font color=#0000ff>__stdcall</font>调用约定：&nbsp;<br>[1]以&#8220;?&#8221;标识函数名的开始，后跟函数名；&nbsp;<br>[2]函数名后面以&#8220;@@YG&#8221;标识参数表的开始，后跟参数表；&nbsp;<br>[3]参数表以代号表示：&nbsp;<br>X--<font color=#0000ff>void</font>，&nbsp;<br>D--<font color=#0000ff>char</font>，&nbsp;<br>E--<font color=#0000ff>unsigned</font>&nbsp;<font color=#0000ff>char</font>，&nbsp;<br>F--<font color=#0000ff>short</font>，&nbsp;<br>H--<font color=#0000ff>int</font>，&nbsp;<br>I--<font color=#0000ff>unsigned</font>&nbsp;<font color=#0000ff>int</font>，&nbsp;<br>J--<font color=#0000ff>long</font>，&nbsp;<br>K--<font color=#0000ff>unsigned</font>&nbsp;<font color=#0000ff>long</font>，&nbsp;<br>M--<font color=#0000ff>float</font>，&nbsp;<br>N--<font color=#0000ff>double</font>，&nbsp;<br>_N--<font color=#0000ff>bool</font>，&nbsp;<br>....&nbsp;<br>PA--表示指针，后面的代号表明指针类型，如果相同类型的指针连续出现，以&#8220;0&#8221;代替，一个&#8220;0&#8221;代表一次重复；&nbsp;<br>[4]参数表的第一项为该函数的返回值类型，其后依次为参数的数据类型,指针标识在其所指数据类型前；&nbsp;<br>[5]参数表后以&#8220;@Z&#8221;标识整个名字的结束，如果该函数无参数，则以&#8220;Z&#8221;标识结束。&nbsp;其格式为<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#8220;&#63;&#102;&#117;&#110;&#99;&#116;&#105;&#111;&#110;&#110;&#97;&#109;&#101;&#64;&#64;&#89;&#71;&#42;&#42;&#42;&#42;&#42;&#64;&#90;"><u><font color=#0000ff>&#8220;?functionname@@YG*****@Z</font></u></a>&#8221;或&#8220;?functionname@@YG*XZ&#8221;，例如&nbsp;<br><font color=#0000ff>int</font>&nbsp;Test1(<font color=#0000ff>char</font>&nbsp;*var1,<font color=#0000ff>unsigned</font>&nbsp;<font color=#0000ff>long</font>)-----&#8220;?Test1@@YGHPADK@Z&#8221;&nbsp;<br><font color=#0000ff>void</font>&nbsp;Test2()&nbsp;-----&#8220;?Test2@@YGXXZ&#8221;&nbsp;<br><font color=#0000ff>__cdecl</font>调用约定：&nbsp;<br>规则同上面的<font color=#0000ff>_stdcall</font>调用约定，只是参数表的开始标识由上面的&#8220;@@YG&#8221;变为&#8220;@@YA&#8221;。&nbsp;<br><font color=#0000ff>__fastcall</font>调用约定：&nbsp;<br>规则同上面的<font color=#0000ff>_stdcall</font>调用约定，只是参数表的开始标识由上面的&#8220;@@YG&#8221;变为&#8220;@@YI&#8221;。&nbsp;<br>VC++对函数的省缺声明是"__cedcl",将只能被C/C++调用。<br><br>3)用法举例<br>BOOL&nbsp;<font color=#0000ff>_declspec</font>(<font color=#0000ff>dllexport</font>)<font color=#0000ff>_stdcall</font>&nbsp;InstallHook()<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hkb&nbsp;=&nbsp;SetWindowsHookEx(WH_KEYBOARD,&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(HOOKPROC)KeyboardProc,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hins,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color=#0000ff>return</font>&nbsp;TRUE;<br>}<br>
<img src ="http://www.cppblog.com/Cyclopedia/aggbug/67953.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Cyclopedia/" target="_blank">Cyclopedia</a> 2008-11-27 00:18 <a href="http://www.cppblog.com/Cyclopedia/articles/67953.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>