__cdecl 在 C 中的函数参数中有什么用

What's the use of __cdecl in function arguments in C

我正在学习C语言,在学习过程中我发现了一行对我来说很陌生的代码 void PullDown(char **, int, void (__cdecl **)(void)); 我只知道第一个和第二个参数。 我想知道第三个参数。 __cdecl 后面的两个星号有什么用? 我从这个语法中知道 (type_cast *) 所以它与类型转换有关?

__cdecl 是微软编译器支持的C language extension。它明确指定应使用 "cdecl" 调用约定调用函数,这与调用函数前后应如何设置寄存器状态和堆栈的内部结构有关,以便传递参数和return 值。

在您的代码片段中,PullDown 被定义为具有三个参数的函数,前两个参数是 char **int

该函数的最后一个参数 void (__cdecl **)(void) 是一个指针,该指针指向具有 cdecl 调用约定的函数的指针,该函数没有 return 值且不接受任何参数。

为了打破这个声明,我们现在可以完全删除 __cdecl 并为这个参数添加一个变量名:

void (**param)(void)

声明中的*运算符指定它右边的表达式是一个指针,所以这意味着param是一个指针,而且*param也是一个指针(因此 param 是指向指针的指针)。为了理解这个指针指向什么,**param 可以暂时用占位符 foobar 代替,得到以下内容:

void (foobar)(void)

这现在有一对多余的括号,等同于以下内容:

void foobar(void)

这现在看起来像是一个带有 void 参数的 returning void 的常规函数​​声明(没有参数也没有 return 值)。因此 param 是指向具有此签名的函数的指针。

最后,__cdecl应用于它右边的表达式,由于**param代表函数,__cdecl可以加在**param的左边表明此函数具有 cdecl 调用约定:

void (__cdecl **param)(void)

您的代码片段中的参数仅删除了参数名称 param,方法与从 char **paramint param 中删除的方法相同。

一般来说,使用 Visual Studio 编译 C 和 C++ 代码时,cdecl 调用约定应该是默认的,因此明确指定 __cdecl 应该是多余的。然而,有时需要指定一个函数具有 __stdcall 调用约定,并且在处理函数指针时确保 stdcall 函数仅通过 __stdcall 函数指针和cdecl 函数只能通过 __cdecl 函数指针调用(这应该是默认的)。尝试使用错误的调用约定调用函数很可能会使您的程序崩溃或使其处于不确定状态。

第三个参数是指向函数指针的指针。只有一个星号,就是函数指针

__cdecl 是编译器特定的属性,指示必须使用 C 调用约定。参见 this page。如果你只玩 C 或其他编译器,那么你可以忽略它。

也许示例有帮助:

#include <stdio.h>

void PullDown(char **, int, void (**)(void));

int main(int argc, char **argv)
{
    void (*fun)(void);
    PullDown(NULL, 0, &fun);
    fun();
    return 0;
}

void my_function(void)
{
    printf("Hello!\n");
}

void PullDown(char **param1, int param2, void (**param3)(void))
{
    *param3 = my_function;    
}

它打印 "Hello!"

例子中,fun是一个函数指针变量。 fun 的指针被传递给 PullDown() 函数调用。所以PullDown()可以设置my_function()的指针指向fun

  • void (*)(void) 是指向 void func (void).

    类型函数的函数指针
  • void (**)(void)是指向函数指针的指针。可能意味着调用者希望写入此参数,以便将函数指针传递回调用者。

  • void (__cdecl **)(void) 是相同的,但带有非标准扩展名 __cdecl。这指定了函数的调用约定,即谁负责堆叠参数。如果是调用者,则使用 __cdecl,如果是函数,则使用 __stdcall。这两个非标准扩展都常用于 Windows 编程。

    在本例中,它指定了所指向函数的调用约定。

__cdecl 是默认 C/C++ 调用约定的标签(令人惊讶的是,命名为 cdecl)。 简而言之,调用约定是描述如何在汇编中调用函数的一组规则(例如,将参数放入 registers/stack,从 EAX/RAX 寄存器或其他地方获取结果)。 您可以在 corresponding wiki page.

中阅读更多关于这些约定的信息

你得到的是函数 PullDown,它有 3 个参数,第三个参数是一个指针,指向一个应该满足 cdecl 约定的函数。