如何安静C编译器关于函数指针需要任意数量的参数?

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

  void *(*run)(); 

在编译期间,我收到以下关于此的警告:

  warning: function declaration isn't a prototype 

这个警告是良性的,因为指针在我的代码中的许多地方用来调用它指向的函数,一切正常。 但是,我真的想要保持警告。

如果我改成它:

  void *(*run)(void); 

我使用它时会遇到编译错误,因为使用指针的各种函数具有不同数量的参数,并且在括号内表示void告诉编译器它不接受任何参数。

我不能使用va_list或类似的任何东西,因为这只是指向另一个函数的指针,我使用单个指针,因为它保持代码干净和简单。

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

  -Wno-strict-prototypes 

但是如果我能避免它,我宁愿不用标志来禁用编译器警告。

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

代码完美无缺。 我只想让警告消失。

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

您可以像这样强制转换函数指针:

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

注意:

  • 这里没有必要进行显式转换,因为void *总是可以转换为任何指针类型。
  • (*fp)之前的初始“void”是函数指针的返回类型。

你正在尝试干净的事情 – 即让编译器参与检查,但你发明的设计根本就不能干净。 您不能以这种方式在原型检查中涉及编译器,因为您始终必须知道在运行时在此特定情况下传递哪些参数。 编译器无法检查这一点,如果您犯了错误,分段错误即将发生。

但是,如果我记得很清楚,这样的东西也可能在linux内核中使用(?)。 解决方案是拥有一个通用指针(就像你拥有的那样),每次调用特定的函数时,只需将其类型转换为指向特定参数的指针即可。 您可能需要将其类型化为void *首先使编译器再次静音:-)

在C中,当您调用没有原型可见的函数时, 默认参数提升将应用于您传递给函数的所有参数。 这意味着您实际传递的类型不一定与函数接收的类型匹配。

例如

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

这意味着您需要知道您实际调用的函数与促销后传递的参数所隐含的函数具有兼容的签名。

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

请注意,当您使用原型定义函数时,当您将函数指针指定给没有原型的函数指针时,您可以有效地将void(*)(int, int)转换为void(*)()所以在调用函数之前执行反向转换是完全正确和可取的。 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函数指针声明,然后让调用者明确地转换它:

 typedef void *(*run)(); //when calling... void my_foo() {} run r = (run)my_foo; 

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

使用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 *来保存函数指针 – 这种转换未被ISO C标准指定,并且在某些体系结构上可能不可用。

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