0

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

4

4 回答 4

3

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

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

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

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

void (**param)(void)

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

void (foobar)(void)

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

void foobar(void)

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

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

void (__cdecl **param)(void)

代码片段中的参数只是删除了参数名称,就像从andparam中删除它一样。char **paramint param

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

于 2018-06-18T14:51:55.667 回答
0

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

__cdecl是编译器特定的属性,指示必须使用 C 调用约定。请参阅此页面。如果您只使用 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.

于 2018-06-18T14:52:14.743 回答
0
  • void (*)(void)是指向类型函数的函数指针void func (void)

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

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

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

于 2018-06-18T14:52:52.087 回答
0

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

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

于 2018-06-18T14:55:05.940 回答