【问题标题】:What's the use of __cdecl in function arguments in CC中函数参数中__cdecl的用途是什么
【发布时间】:2018-06-18 14:21:22
【问题描述】:

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

【问题讨论】:

  • 如果它只是在没有事先介绍 几个 主题的情况下将其丢给你,那么你确实是在从一个奇怪的资源中学习。
  • void (**)(void) 是一个指针,指向一个函数的指针,该函数的参数列表为空,不返回任何值。 __cdecl 是某种特定于您的编译器的调用约定关键字。

标签: c cdecl


【解决方案1】:

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

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

函数的最后一个参数void (__cdecl **)(void) 是一个指针,该指针指向一个具有 cdecl 调用约定的函数的指针,该函数没有返回值,也没有参数。

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

void (**param)(void)

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

void (foobar)(void)

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

void foobar(void)

这现在看起来像一个常规函数声明,返回 voidvoid 参数(没有参数也没有返回值)。因此param 是一个指向具有此签名的函数的指针。

最后,__cdecl适用于它右边的表达式,由于**param代表函数,__cdecl可以加到**param的左边,表示这个函数有cdecl调用约定:

void (__cdecl **param)(void)

您的代码 sn-p 中的参数只是删除了参数名称 param,与从 char **paramint param 中删除的方式相同。

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

【讨论】:

    【解决方案2】:

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

    __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;    
    }
    

    它打印“你好!”

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

    【讨论】:

      【解决方案3】:
      • void (*)(void) 是指向void func (void) 类型函数的函数指针。

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

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

        在这种情况下,它指定了指向函数的调用约定。

      【讨论】:

        【解决方案4】:

        __cdecl 是默认 C/C++ 调用约定的标签(令人惊讶的是,命名为 cdecl)。 简单来说,调用约定是描述如何在汇编中调用函数的一组规则(例如,将参数放入寄存器/堆栈,从 EAX/RAX 寄存器或其他地方获取结果)。 您可以在corresponding wiki page 中了解更多关于这些约定的信息。

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-11-07
          • 1970-01-01
          • 2022-11-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多