DLL调用约定与名称修饰详解

5星 · 超过95%的资源 需积分: 17 4 下载量 190 浏览量 更新于2024-09-18 收藏 70KB DOC 举报
"DLL中调用约定和名称修饰是编程中的关键概念,特别是在使用动态链接库(DLL)时。调用约定定义了函数调用的规则,包括参数传递方式、堆栈清理责任等,而名称修饰则涉及到C++中为了支持函数重载和操作符重载对函数名进行的特殊编码。本文将深入探讨这两种机制,特别是针对Microsoft VC++ 6.0环境下的几种主要调用约定,并通过实例解析__cdecl调用约定的工作原理。" 在编程语言中,调用约定是确保函数调用正确执行的关键规范。它定义了参数如何在调用者和被调用者之间传递,以及调用结束后堆栈的清理方式。在C++中,由于支持函数重载和操作符重载,编译器会使用名称修饰技术,对函数的实际名称进行编码,使得每个函数的入口点具有唯一标识,即使它们的名称相同但参数列表不同。 名称修饰,也称为名称改编或名称装饰,是C++编译器特有的功能。例如,当C++编译器遇到一个名为`Add`的函数,接受两个整数参数时,它会生成一个不同于简单`Add`的内部名称,这个新名称包含了函数签名的信息。不同的编译器可能使用不同的名称修饰方案,这可能导致不同编译器生成的DLL在互相调用时出现问题。为了避免这种问题,当需要从其他语言(如Visual Basic、Pascal或Fortran)调用C/C++编写的DLL函数时,必须使用未修饰的函数名,并指定正确的调用约定。 在Microsoft VC++ 6.0中,有多种调用约定可供选择,其中最常见的是: 1. **__cdecl**:这是C/C++和MFC的默认调用约定。它遵循从右到左的参数入栈顺序,并由调用者负责清理堆栈。由于此约定允许可变参数函数(如`printf`),因此在需要自定义参数清理时使用。然而,由于每个函数都需要包含清理堆栈的代码,这可能导致生成的可执行文件较大。 以下是一个使用__cdecl的例子: ```cpp int __cdecl Add(int a, int b); // 函数声明 int main() { Add(1, 2); // 函数调用 } int __cdecl Add(int a, int b) { // 函数实现 return (a + b); } ``` 在反汇编代码中可以看到,参数首先被压入堆栈(从右到左),然后在函数体结束时由调用者负责清理。 除了__cdecl,还有其他几种调用约定,如: 2. **__stdcall**:参数按从左到右的顺序入栈,被调用者负责清理堆栈。在Windows API中广泛使用。 3. **__fastcall**:通常,参数通过寄存器传递,以提高调用速度。剩余参数(如果有的话)按从右到左的顺序入栈,通常由被调用者清理堆栈。 4. **__thiscall**:C++类成员函数的默认调用约定,参数通过ECX寄存器传递,this指针放在第一个参数的位置。 了解并正确使用这些调用约定对于编写高效、兼容的DLL至关重要。同时,避免名称修饰对于跨语言调用也是必要的。在编写DLL时,程序员应当明确导出函数的调用约定,并使用`extern "C"`来防止C++的名称修饰,以确保与其他语言的兼容性。