0%

函数调用约定

函数调用约定分类

函数调用约定(Calling Convention)是规定函数在栈上的参数传递和清理方式的约定。不同的编程语言和编译器可能使用不同的调用约定,主要有以下几种常见的函数调用约定:

  1. StdCall(标准调用约定)

    • 参数由调用者在栈上顺序压入,由被调用函数负责清理栈
    • 函数名称在链接时由名称修饰器(name decoration)进行修饰,用于指定函数的参数数量和类型。可以支持函数重载
    • Windows API 中的大部分函数使用标准调用约定。
  2. Cdecl(C 调用约定)

    • 参数由调用者在栈上顺序压入,由调用者负责清理栈
    • 函数名称在链接时不进行修饰。不支持函数重载
    • 在 C 和 C++ 中常用的默认调用约定。
  3. FastCall(快速调用约定)

    • 将一些参数(通常是寄存器数量限制内的参数)存放在寄存器中传递,剩余的参数由调用者在栈上压入。
    • 寄存器用于存储参数的寄存器数量和顺序由编译器约定。
    • 函数名称在链接时不进行修饰。
    • 可以提高函数调用的性能。
  4. ThisCall(成员函数调用约定)

    • 参数由调用者在栈上顺序压入,但第一个参数(this 指针)通过寄存器传递。
    • 其余参数由调用者负责清理栈。
    • 常用于非静态成员函数,this 指针通过 ECX 寄存器传递。
  5. Winapi(Windows API 调用约定)

    • 与 StdCall 相同,参数由调用者在栈上顺序压入,由被调用函数负责清理栈。
    • 函数名称在链接时不进行修饰。
    • 在 Windows API 中,Winapi 通常是 StdCall 的同义词。
  6. Pascal(帕斯卡调用约定)

    • 参数由调用者在栈上逆序压入,由被调用函数负责清理栈。
    • 函数名称在链接时不进行修饰。
    • 常用于 Delphi 等编程语言。

选择适当的函数调用约定主要取决于编程语言、平台和编译器的要求。通常情况下,你无需显式指定函数的调用约定,编译器会根据语言和平台的规范自动选择合适的调用约定。但在某些情况下,例如与非托管代码交互或使用特定的编译器选项时,可能需要手动指定函数的调用约定。

顺序压入(Arguments Pushed in Order)和逆序压入(Arguments Pushed in Reverse Order)是函数调用过程中参数在栈上的压入顺序。

  1. 顺序压入:在顺序压入的调用约定中,函数参数按照从左到右的顺序依次压入栈中。也就是说,第一个参数被压入栈的位置最低,最后一个参数被压入栈的位置最高。这意味着栈的顶部是最后一个参数的位置。 例如,假设有一个函数 void MyFunction(int a, int b, int c),使用顺序压入的调用约定时,参数 a 会被首先压入栈,然后是参数 b,最后是参数 c。在函数内部,通过访问栈上的相对偏移量,可以获取到相应的参数值。
  2. 逆序压入:在逆序压入的调用约定中,函数参数按照从右到左的顺序依次压入栈中。也就是说,第一个参数被压入栈的位置最高,最后一个参数被压入栈的位置最低。这意味着栈的顶部是第一个参数的位置。 例如,假设有一个函数 void MyFunction(int a, int b, int c),使用逆序压入的调用约定时,参数 c 会被首先压入栈,然后是参数 b,最后是参数 a。在函数内部,通过访问栈上的相对偏移量,可以获取到相应的参数值。

顺序压入和逆序压入是两种不同的参数传递方式,而具体使用哪种方式取决于函数调用约定的规定。不同的编程语言、平台和编译器可能使用不同的参数压入顺序。因此,在编写和调用函数时,需要了解所使用的编程语言和调用约定的规范,以正确地传递参数。

在 C++ 中指定函数的调用约定

1
2
3
4
5
6
7
8
__cdecl void MyFunction(int a, int b);
__stdcall void MyFunction(int a, int b);
__fastcall void MyFunction(int a, int b);
class MyClass
{
public:
__thiscall void MyMethod(int a, int b);
};

在 C++ 源码中,可以使用一些特定的关键字和修饰符来表示一个函数是导出函数。具体的表示方式取决于所使用的编译器和平台。

1
2
3
4
5
6
7
8
9
#ifdef _WIN32
#define DLL_EXPORT __declspec(dllexport) // Microsoft Visual C++ 编译器的扩展,用于指定一个函数是导出函数
#define DLL_IMPORT __declspec(dllimport)
#else
#define DLL_EXPORT
#define DLL_IMPORT
#endif

DLL_EXPORT void MyExportedFunction();