参见:http://blog.twofei.com/cc/impl/calling-convension.html

调用约定(Calling Convention)是指在程序设计语言中为了实现函数调用而建立的一种协议.
这种协议规定了该语言的函数中的参数传送方式,参数是否可变和由谁来处理堆栈等问题. 不同的语
言定义了不同的调用约定.

在C++中,为了允许操作符重载和函数重载,C++编译器往往按照某种规则改写每一个入口点的符号名,
以便允许同一个名字(具有不同的参数类型或者是不同的作用域)有多个用法,而不会打破现有的基于C的链
接器. 这项技术通常被称为名称改编(Name Mangling)或者名称修饰(Name Decoration). 许多C++编译
器厂商选择了自己的名称修饰方案.

因此,为了使其它语言编写的模块(如Visual Basic应用程序,Pascal或Fortran的应用程序等)可以调
用C/C++编写的函数,必须使用正确的调用约定来导出函数,并且不要让编译器对要导出的函数进行任何名称修饰.


1.调用约定(Calling Convention)
    调用约定用来处理决定函数参数传送时入栈和出栈的顺序(由调用者还是被调用者把参数弹出栈),以及编译
器用来识别函数名称的名称修饰约定等问题. 在Microsoft VC++ 6.0中定义了下面几种调用约定,我们将结合汇
编语言来一一分析它们:

  *** C语言中的调用约定 ***
    1. __cdecl
        __cdecl是C/C++(非类成员函数以及静态类成员函数)程序默认使用的调用约定,也可以在函数声明时
    加上 __cdecl 关键字 来手工指定. 采用__cdecl约定时,函数参数按照 从右到左 顺序入栈,并且由调用
    函数者把参数弹出栈以清理堆栈. 因此,实现可变参数的函数只能使用该调用约定. 由于每一个使用
    __cdecl约定的函数都要包含清理堆栈   的代码,所以产生的可执行文件大小会稍大一丁点.

下面将通过一个具体实例来分析__cdecl约定:

        int __cdecl Add(int a,int b)
{
return a+b;
} void main(void)
{
Add(,);
}

  函数调用处反汇编代码如下:

        main:
0134474E 6A push ;压入参数2(最右边)
6A push ;压入参数1(右边倒数第2个)
E8 F5 CC FF FF call Add (0134144Ch) ;调用目标函数
C4 add esp, ;恢复堆栈 Add:
00A8291E 8B mov eax,dword ptr [a]
00A82921 0C add eax,dword ptr [b]
00A8292A C3 ret ;被调用者不清理堆栈

  由此可见,在__cdecl中:
        1.参数从右到左依次传递
        2.参数个数等于函数声明时的个数(变参除外)
        3.由调用者清理堆栈


2. __stdcall
        __stdcall调用约定用于调用Win32 API函数 Win32API的WINAPI/CALLBACK等都是它. 采用__stdcall
    约定时,函数参数按照 从右到左 的顺序入栈,被调用的函数在返回前清理参数堆栈,函数参数个数固定.
    由于函数体本身知道传进来的参数个数,因此被调用的函数可以在返回前用一条ret n指令直接清理传递参数的堆栈.

还是上面那个例子,将__cdecl约定换成__stdcall:

        int __stdcall Add(int a,int b)
{
return a+b;
} void main(void)
{
Add(,);
}

函数调用处反汇编代码(从简):

        main:
;Add(1,2);
0131474E 6A push ;参数依然是从右到左传递
6A push
E8 FA CC FF FF call Add (01311451h)
C0 xor eax,eax ;调用函数并不清理堆栈
0131476C C3 ret int __stdcall Add(int a,int b):
0131291E 8B mov eax,dword ptr [a] ;完成相加操作
0C add eax,dword ptr [b]
0131292A C2 ret ;返回时清理参数栈

      由此可见,在__stdcall中:
           1.函数参数从右到左传递
           2.参数个数等于函数声明时的个数
           3.由被调用者清理参数栈


3. __fastcall
        __fastcall约定用于对性能要求非常高的场合.
        __fastcall约定将函数的从左边开始的两个大小不大于4个字节(DWORD)的参数分别放在ECX和EDX寄存器,
    其余的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的堆栈.
        (暂无代码)
        1.函数参数从右到左传递
        2.由被调用者清理堆栈


  *** C++中的调用约定 ***
        以上三种C语言中可用的调用约定均可以用于修饰C++中的非静态成员函数,只是会将this指针当作一个普通的
    参数一样压入(__cdecl,__stdcall)/传递到ECX(EDX)(__fastcall).
        __thiscall调用约定是C++中专用的,是非静态类成员函数的默认调用约定(注:在VC6中没有此关键字).
    采用 __thiscall约定时,函数参数按照从右到左的顺序入栈,被调用的函数在返回前清理传送参数的栈,
    只是它是通过ECX寄存器传送一个额外的参数:this指针

这次的例子中将定义一个类,并在类中定义一个成员函数,代码如下:

        class A
{
public:
int __cdecl Add1(int a,int b)
{
return a+b;
} int __stdcall Add2(int a,int b)
{
return a+b;
} int __fastcall Add3(int a,int b)
{
return a+b;
} int __thiscall Add4(int a,int b)
{
return a+b;
}
}; void main(void)
{
A a;
a.Add1(,);
a.Add2(,);
a.Add3(,);
a.Add4(,);
}

再来看看这个稍微复杂一点的C++类调用的反汇编代码:
        1. 除 Add3 的__fastcall外其余全部通过从右到左参数
        2. 除默认的函数需要的参数个数外, 额外传入一个this指针
        3. 清理堆栈的方式保持不变

            a.Add1(,);
001C1528 6A push
001C152A 6A push
001C152C 8D F7 lea eax,[a]
001C152F push eax ;传入this指针
001C1530 E8 1B FB FF FF call A::Add1 (01C1050h)
001C1535 C4 0C add esp,0Ch ;由于多传入了一个this,所以是0Ch a.Add2(,);
001C1538 6A push
001C153A 6A push
001C153C 8D F7 lea eax,[a] ;传入this指针
001C153F push eax
001C1540 E8 DD FB FF FF call A::Add2 (01C1122h) a.Add3(,);
001C1545 6A push
001C1547 BA mov edx, ;将参数保存到edx
001C154C 8D 4D F7 lea ecx,[a] ;将this保存到ecx
001C154F E8 FB FF FF call A::Add3 (01C10B4h) a.Add4(,);
001C1554 6A push
001C1556 6A push
001C1558 8D 4D F7 lea ecx,[a] ;由ecx传递this指针
001C155B E8 B4 FA FF FF call A::Add4 (01C1014h) 类成员反汇编代码(仅保留返回部分,其余地方同C语言中一样):
__cdecl: 001C141A C3 ret ;__cdecl由调用者清理堆栈
__stdcall: 001C145A C2 0C ret 0Ch ;加上this,总共3个参数
__fastcall: 001C14A2 C2 ret ;this和其中一个由寄存器传递
__thiscall: 001C14EF C2 ret ;this通过ecx传递,堆栈不受影响

<名称修饰,后面补上>
----------------
女孩不哭 @ 2013-09-11 00:18:11 @ http://www.cnblogs.com/nbsofer

C/C++/动态链接库DLL中函数的调用约定与名称修饰的更多相关文章

  1. DLL中调用约定和名称修饰(一)

    DLL中调用约定和名称修饰(一) 调用约定(Calling Convention)是指在程序设计语言中为了实现函数调用而建立的一种协议.这种协议规定了该语言的函数中的参数传送方式.参数是否可变和由谁来 ...

  2. C/C++:函数的调用约定(Calling Convention)和名称修饰(Decorated Name)以及两者不匹配引起的问题

    转自:http://blog.csdn.net/zskof/article/details/3475182 注:C++有着与C不同的名称修饰,主要是为了解决重载(overload):调用约定则影响函数 ...

  3. DLL模块例1:使用.def模块导出函数,规范修饰名称,显示连接调用dll中函数

    以下内容,我看了多篇文章,整合在一起,写的一个例子,关于dll工程的创建,请参考博客里另一篇文章:http://www.cnblogs.com/pingge/articles/3153571.html ...

  4. 动态链接库DLL导出函数并导入使用

    动态链接库DLL导出函数并导入使用 本文完全参考自<vs2008制作dll笔记,回带值样例>. 首先制作DLL文件,在vs2010中新建Win32控制台项目,选择DLL选项,简历头文件,源 ...

  5. object C—类中函数的调用

    Object C-类中函数的调用 创建,三个类.然后,在代码中调用相同名字的函数.观察他们的调用次序. @interface test : NSObject - (void)print; @end @ ...

  6. 关于js中函数的调用问题

    js中函数的调用方法 1.直接调用 函数名(参数): 2.通过指向函数的变量去调用 例如: var myval = 函数名: 此刻 myval是指向函数的一个指针: myval(实际参数):此刻调用的 ...

  7. JavaScript中函数的调用

    JavaScript中函数的调用 制作人:全心全意 在JavaScript中,函数定义后并不会自动执行,要执行一个函数需要在特定的位置调用该函数,调用函数需要创建调用语句,调用语句包含函数名称和参数. ...

  8. JavaScript中函数的调用!

    JavaScript中函数的调用! 1 普通函数 // 1 普通函数 function fn() { console.log(123); } // 函数名 + 一个小括号! 或者 函数名.call() ...

  9. C语言中函数的调用方式

    第一眼看到这样一个题目的我,心想除了如下的直接调用还能怎么调用呢? 1 void fun(void) 2 { 3 ...... 4 //你的代码 5 ..... 6 } 7 int main(void ...

随机推荐

  1. OpenGL ES 3.0之Fragment buffer objects(FBO)详解(一)

    片段操作图 这篇文章将介绍从写入帧缓冲和读取帧缓冲的方式. Buffers(缓冲) OpenGL ES支持三种缓冲: OpenGL ES •• Color buffer颜色缓冲 •• Depth bu ...

  2. 使用Nexus管理maven仓库,setting文件理解

    来到新公司对很多陌生的技术一头雾水,以前在工作中没有真正使用过maven,于是强迫自己蛋定下来一个一个的突破,下面是我对maven的setting配置文件的理解,由于是现学的,难免可能会理解偏差,还请 ...

  3. C#.NET常见问题(FAQ)-如何判断某个字符是否为汉字

    字符强制转换成int可以判断字符数值大小,在下面所示范围内的就是中文   此外还可以判断是否是数字或者字母,用char.IsLetter和char.IsDigit方法   从先这个范例可以看出,中文也 ...

  4. url文件的格式

    [DEFAULT]BASEURL= [InternetShortcut]URL=WorkingDirectory=ShowCommand=IconIndex=IconFile=Modified=Hot ...

  5. HTML DOM defaultValue 属性

    定义和用法 defaultValue 属性设置或返回文本框的初始内容. 注释:文本框的初始值是位于 <textarea> 和 </textarea> 标签之间的文本.在表单被重 ...

  6. Word2007视频教程

    超级好教程 http://v.youku.com/v_show/id_XMTAwOTgwNTIw.html 视频: oeasy教你玩转office系列之Word视频教程01 http://v.youk ...

  7. winf

    真的,先亮注册码!!(直接复制即可) 注册码: <第1组> 用户名:大眼仔~旭(Anan) 注册码:01000000007002140V <第2组> 用户名:大眼仔~旭(Ana ...

  8. hadoop lzo应用

    几种压缩方式对比: LZO example: https://github.com/twitter/hadoop-lzo/blob/master/src/test/java/com/hadoop/ma ...

  9. JDK5.0 特性-线程同步装置之Semaphore

    来自:http://www.cnblogs.com/taven/archive/2011/12/17/2291474.html import java.util.ArrayList; import j ...

  10. hdu 3308 LCIS(线段树区间合并)

    题目链接:http://acm.hdu.edu.cn/showproblem.php? pid=3308 LCIS Time Limit: 6000/2000 MS (Java/Others)     ...