调用约定(Calling Convention)是指在程序设计语言中为了实现函数调用而建立的一种协议。这种协议规定了该语言的函数中的参数传送方式、参数是否可变和由谁来处理堆栈等问题。不同的语言定义了不同的调用约定。
在C++中,为了允许操作符重载和函数重载,C++编译器往往按照某种规则改写每一个入口点的符号名,以便允许同一个名字(具有不同的参数类型或者是不同的作用域)有多个用法,而不会打破现有的基于C的链接器。这项技术通常被称为名称改编(Name Mangling)或者名称修饰(Name Decoration)。许多C++编译器厂商选择了自己的名称修饰方案。
因此,为了使其它语言编写的模块(如Visual Basic应用程序、Pascal或Fortran的应用程序等)可以调用C/C++编写的DLL的函数,必须使用正确的调用约定来导出函数,并且不要让编译器对要导出的函数进行任何名称修饰。
1.调用约定(Calling Convention)
调用约定用来处理决定函数参数传送时入栈和出栈的顺序(由调用者还是被调用者把参数弹出栈),以及编译器用来识别函数名称的名称修饰约定等问题。在Microsoft VC++ 6.0中定义了下面几种调用约定,我们将结合汇编语言来一一分析它们:
1、__cdecl
__cdecl是C/C++和MFC程序默认使用的调用约定,也可以在函数声明时加上__cdecl关键字来手工指定。采用__cdecl约定时,函数参数按照从右到左的顺序入栈,并且由调用函数者把参数弹出栈以清理堆栈。因此,实现可变参数的函数只能使用该调用约定。由于每一个使用__cdecl约定的函数都要包含清理堆栈的代码,所以产生的可执行文件大小会比较大。__cdecl可以写成_cdecl。
下面将通过一个具体实例来分析__cdecl约定:
在VC++中新建一个Win32 Console工程,命名为cdecl。其代码如下:
int __cdecl Add(int a, int b); //函数声明
void main()
{
Add(1,2); //函数调用
}
int __cdecl Add(int a, int b) //函数实现
{
return (a + b);
}
函数调用处反汇编代码如下:
;Add(1,2);
push 2 ;参数从右到左入栈,先压入2
push 1 ;压入1
call @ILT+0(Add) (00401005) ;调用函数实现
add esp,8 ;由函数调用清栈
2、__stdcall
__stdcall调用约定用于调用Win32 API函数。采用__stdcal约定时,函数参数按照从右到左的顺序入栈,被调用的函数在返回前清理传送参数的栈,函数参数个数固定。由于函数体本身知道传进来的参数个数,因此被调用的函数可以在返回前用一条ret n指令直接清理传递参数的堆栈。__stdcall可以写成_stdcall。
还是那个例子,将__cdecl约定换成__stdcall:
int __stdcall Add(int a, int b)
{
return (a + b);
}
函数调用处反汇编代码:
; Add(1,2);
push 2 ;参数从右到左入栈,先压入2
push 1 ;压入1
call @ILT+10(Add) (<chmetcnv tcsc="0" numbertype="1" negative="false" hasspace="false" sourcevalue="40100" unitname="f" w:st="on">0040100f</chmetcnv>) ;调用函数实现
函数实现部分的反汇编代码:
;int __stdcall Add(int a, int b)
push ebp
mov ebp,esp
sub esp,40h
push ebx
push esi
push edi
lea edi,[ebp-40h]
mov ecx,10h
mov eax,0CCCCCCCCh
rep stos dword ptr [edi]
;return (a + b);
mov eax,dword ptr [ebp+8]
add eax,dword ptr [ebp+0Ch]
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret 8 ;清栈
3、__fastcall
__fastcall约定用于对性能要求非常高的场合。__fastcall约定将函数的从左边开始的两个大小不大于4个字节(DWORD)的参数分别放在ECX和EDX寄存器,其余的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的堆栈。__fastcall可以写成_fastcall。
依旧是相类似的例子,此时函数调用约定为__fastcall,函数参数个数增加2个:
int __fastcall Add(int a, double b, int c, int d)
{
return (a + b + c + d);
}
函数调用部分的汇编代码:
;Add(1, 2, 3, 4);
push 4 ;后两个参数从右到左入栈,先压入4
mov edx,3 ;将int类型的3放入edx
push 40000000h ;压入double类型的2
push 0
mov ecx,1 ;将int类型的1放入ecx
call @ILT+0(Add) (00401005) ;调用函数实现
函数实现部分的反汇编代码:
; int __fastcall Add(int a, double b, int c, int d)
push ebp
mov ebp,esp
sub esp,48h
push ebx
push esi
push edi
push ecx
lea edi,[ebp-48h]
mov ecx,12h
mov eax,0CCCCCCCCh
rep stos dword ptr [edi]
pop ecx
mov dword ptr [ebp-8],edx
mov dword ptr [ebp-4],ecx
;return (a + b + c + d);
fild dword ptr [ebp-4]
fadd qword ptr [ebp+8]
fiadd dword ptr [ebp-8]
fiadd dword ptr [ebp+10h]
call __ftol (004011b8)
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret 0Ch ;清栈
关键字__cdecl、__stdcall和__fastcall可以直接加在要输出的函数前,也可以在编译环境的Setting...->C/C++->Code Generation项选择。它们对应的命令行参数分别为/Gd、/Gz和/Gr。缺省状态为/Gd,即__cdecl。当加在输出函数前的关键字与编译环境中的选择不同时,直接加在输出函数前的关键字有效。
相关推荐
### 函数调用规则概述 ...例如,在 C 和 Delphi 之间进行 DLL 调用时,明确调用约定能够避免许多潜在的问题。此外,对于那些需要手工编写汇编代码的情况,清楚地知道哪一方负责清理堆栈可以避免内存泄漏等问题的发生。
### DLL中调用约定与名称修饰详解 #### 一、调用约定(Calling Convention) 调用约定是在程序设计语言中为了实现函数调用而建立的一种协议。它规定了该语言的函数中的参数传递方式、参数是否可变以及由谁来处理...
本文将详细讲解两种常见的函数调用约定:__stdcall和__cdecl。 首先,__stdcall是C++中的标准调用方式,它的主要特征是参数从右到左入栈,即参数在函数调用时逆序放入堆栈。当函数返回时,使用`retn x`指令来清理...
_stdcall、_cdecl和_fastcall 的区别.zip
### MFC中_stdcall调用约定 在C和C++编程中,调用约定(call convention)定义了如何在函数调用期间传递参数、清理栈空间等规则。不同的调用约定适用于不同的场景,其中`_stdcall`是较为常见的一种。本文将深入探讨`_...
本文将深入探讨两种常见的调用约定:`__stdcall` 和 `__cdecl`,这两种约定在C++和Windows API编程中尤其常见。 首先,`__cdecl` 是C Declaration的缩写,它是C语言默认的函数调用约定。在`__cdecl`约定中,参数按...
本文将深入探讨三种常见的函数调用约定:`__stdcall`、`__cdecl` 和 `__fastcall`,并对比它们的特点和适用场景。 首先,`__cdecl` 是C语言默认的调用约定,也是大多数编译器默认使用的约定。在`__cdecl`中,函数的...
这个实例"实例01 从外部Dll中调用子窗体.zip_where7ck_从外部DLL中调用子窗体"提供了一个具体的操作示例,主要讲解了如何在主应用程序中通过外部DLL来调用和显示子窗体。 首先,我们要理解DLL的作用。DLL是一种可...
标题中的“TEST DLL (__stdcall)”指的是一个关于创建和使用使用`__stdcall`调用约定的动态链接库(DLL)的技术主题。在Windows操作系统中,DLL是一种可共享的代码库,程序可以在运行时调用其中的函数来实现特定...
解决这些问题的方法包括明确指定函数的调用约定(如`__declspec(dllexport)`或`__declspec(dllimport)`),在C++中使用`extern "C"`来避免名称修饰,或者确保使用与库相同的编译器和编译选项。 理解并掌握这些规则...
如果不匹配,可能需要使用`typedef`来定义新的类型,或者使用`__stdcall`和`__cdecl`等关键字来指定调用约定。 4. **错误处理和资源释放**:在调用DLL函数后,记得检查返回值以处理可能出现的错误。同时,别忘了...
这篇文档将深入探讨如何在Visual Basic ...这涉及到理解Cdecl调用约定,创建和编译DLL,以及在VB中正确地声明和使用这些函数。通过这种方式,开发者可以利用C和C++的高性能特性,同时利用VB的易用性和丰富的GUI工具。
Windows调用约定是指Windows平台下函数调用时的规则和约定,它是编译器在生成函数调用代码时遵循的一套机制,以确保调用方与被调用方之间能够正确地传递参数和处理函数返回值。在C++和C语言中,这些调用约定对程序的...
调用约定和名称修饰规则对于理解函数如何被调用及其名称如何在编译期间被处理至关重要。了解这些概念有助于开发者避免因调用约定或名称修饰不匹配导致的链接错误和其他运行时问题。在实际开发过程中,合理选择和应用...
`__cdecl`(C 默认调用方式)是最常用的调用约定之一,在C和C++程序中默认使用此约定。其特点包括: - 参数从右向左压栈。 - 函数调用者负责清理栈,即调用者在函数返回后清理压栈的参数。 - 可变参数列表的函数始终...
本文档详细介绍了C++builder调用VC编写的DLL的过程,并对__cdecl、__fastcall、__stdcall三种调用约定的解析。 在函数调用过程中,堆栈的使用是非常重要的。C++builder和VC在调用DLL时,使用了不同的调用约定,导致...
### 函数调用约定解析及DLL中调用约定详解 #### 一、引言 本文旨在深入探讨函数调用约定的基础知识以及其在不同编程环境中的应用,特别是针对DLL(动态链接库)中的函数调用约定。理解这些概念对于编写高效、可...