繁体   English   中英

关于 function 指针采用任意数量的 arguments,如何使 C 编译器安静下来?

[英]How do I quiet the C compiler about a function pointer takes any number of arguments?

我在一个struct中有一个 function 指针,该指针在运行时动态设置为我代码中不同位置的另一个 function 的地址。 它在我的 header 文件中定义如下:

    void *(*run)();

在编译期间,我收到以下警告:

    warning: function declaration isn't a prototype

这个警告是良性的,因为在我的代码中的许多地方都使用了指针来调用它指向的 function,并且一切正常。 但是,我真的很想消除警告。

如果我将其更改为:

    void *(*run)(void);

无论我使用它,我都会遇到编译错误,因为使用指针的各种函数具有不同数量的 arguments,并且括号内的void告诉编译器它不接受 arguments。

我不能使用va_list或任何类似的东西,因为这只是指向另一个 function 的指针,我对它们都使用单个指针,因为它使代码保持简洁。

我可以通过将其添加到我的编译器标志来使警告静音:

    -Wno-strict-prototypes

但是,如果可以避免的话,我宁愿不必禁用带有标志的编译器警告。

所以我的问题是:我如何在代码中注释这个 function 指针,以使编译器对它接受任意数量的任何类型的 arguments 这一事实感到满意?

该代码完美运行。 我只想要 go 的警告。

将指针存储为void *并在必要时转换为适当的 function 指针类型? 请记住,将一种类型的 function 指针调用为另一种类型并不一定安全,因此您开始使用的警告并非完全无效。

您可以像这样转换 function 指针:

void *genericPointer = ...;
void (*fp)(int, int) = genericPointer;
fp(123, 456);

注意:

  • 这里不需要显式转换,因为void *总是可以转换为任何指针类型。
  • (*fp)之前的初始“void”是 function 指针的返回类型。

您正在尝试做干净的事情 - 即让编译器参与检查,但是您发明的设计根本无法按照其原理进行清洁。 您不能以这种方式让编译器参与原型检查,因为您始终必须知道在运行时的这种特殊情况下要传递哪些参数。 编译器无法检查这一点,如果你犯了错误,就会出现分段错误。

但如果我没记错的话,linux kernel(?)中也可能使用过类似的东西。 解决方案是拥有一个通用指针(就像您拥有的那样),每次调用特定的 function 时,您只需将其类型转换为指向 function 的指针,并带有特定的 ZDBC11CAA5BDA99F77E6FB4DABD882E7。 您可能需要先将其类型转换为 void * 以再次使编译器静音:-)

In C, when you call a function without a prototype visible, default argument promotions are applied to all of the arguments that you pass to the function. 这意味着您实际传递的类型不一定与 function 接收的类型匹配。

例如

void (*g)();
void f()
{
    float x = 0.5;
    g(x); // double passed
}

这意味着您需要知道您实际调用的 function 与您在升级后传递的 arguments 所暗示的签名兼容。

鉴于您在任何情况下都需要知道这一点,您必须知道在使用 function 指针的调用站点上调用的实际 function 的 function 签名。 有了这些知识,使用具有正确原型的 function 指针通常更简单、更清晰,并且您可以完全避免默认参数提升。

请注意,当您使用原型定义函数时,当您将指向 function 的指针分配给没有原型的 function 指针时,您可以有效地将void(*)(int, int)转换为void(*)()因此在调用 function 之前执行反向转换是完全正确和可取的。 gcc 允许这两种转换而不会发出任何警告。

例如

void PerformCall( void(*p)() )
{
    if (some_condition)
    {
        // due to extra knowledge I now know p takes two int arguments
        // so use a function pointer with the correct prototype.
        void(*prototyped_p)(int, int) = p;
        prototyped_p( 3, 4 );
    }
}

尝试 typedefing function 指针声明,然后让调用者显式地转换它:

typedef void *(*run)();

//when calling...
void my_foo() {}

run r = (run)my_foo;

如果已知不同的 function 签名,请使用union 否则,使用void (*)(void)类型的指针(实际上,任何 function 指针类型都可以)来保存通用指针并在设置值和调用代码时转换为正确的类型。

使用union的示例:

union run_fn
{
    void *(*as_unary)(int);
    void *(*as_binary)(int, int);
};

struct foo
{
    union run_fn run;
};

void *bar(int, int);
struct foo foo;

foo.run.as_binary = bar;
void *baz = foo.run.as_binary(42, -1);

使用显式强制转换的示例:

struct foo
{
    void (*run)(void);
};

void *bar(int, int);
struct foo foo;

foo.run = (void *(*)(int, int))bar;
void *baz = ((void *(*)(int, int))foo.run)(42, -1);

不要使用void *来保存 function 指针 - ISO C 标准未指定此类转换,并且可能在某些架构上不可用。

忽略警告并按原样使用代码实际上也是一种可能性,但请记住,任何 function 参数都将受到默认参数提升的影响,提升的 arguments 与声明的参数正确匹配是您的责任。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM