为什么“int (*)(float)”指向“int foo()”会触发警告,而“int (*)(double)”则不会触发警告?

jus*_*bie 53 c function-pointers language-lawyer

我有这段代码:

int foo() { return 0; }
int main()
{
    int (*float_function)(float) = foo;
}
Run Code Online (Sandbox Code Playgroud)

x86-64 GCC 12.2当使用, with编译时-Wall,它会产生警告(链接):

警告:从不兼容的指针类型“int (*)()”初始化“int (*)(float)”[-Win兼容指针类型]

但是,当我从 更改floatdouble链接)时:

int foo(){ return 0;}
int main()
{
    int (*double_function)(double) = foo;
}
Run Code Online (Sandbox Code Playgroud)

现在警告消失了。

但我认为这两者都应该受到警告。

我有什么地方说错了吗?为什么 GCC 不抱怨第二个例子?

int*_*jay 59

int foo()声明时未指定其参数。这是一个过时的功能,允许您使用任何参数调用它。调用该函数时,整数参数将提升为int(如果需要),float参数将提升为double

因此,该函数无法接收参数float,这使得它与 不兼容int (*)(float),但与 不兼容int (*)(double)

如果您想要一个不带参数的函数,请将其声明为 ,int foo(void)这将使其与两者不兼容。

请注意,即使使用double,代码也不是有效的 C,因为int foo() {...}它是函数定义,因此编译器知道它没有参数(有关标准参考,请参阅下面 chux 的注释)。大多数编译器仍然允许它。

如果您用声明替换它int foo();并将定义放在其他地方,那么上面的内容是正确的。在这种情况下,相关标准引用(C17 6.7.6.3/15)为:

为了使两种函数类型兼容,[...]如果一种类型具有参数类型列表,而另一种类型由不属于函数定义的一部分且包含空标识符列表的函数声明符指定,[...] .] 每个参数的类型应与应用默认参数提升所产生的类型兼容。

  • 很好的解释。声明函数时未指定指定参数通常会导致混乱。可以通过启用警告 [`-Wstrict-prototypes`](http://gcc.gnu.org/onlinedocs/gcc-4.4.0/gcc/Warning-Options.html) 来捕获它(不,`-Wall`不启用所有警告)。 (8认同)
  • interjay,C11 § 6.7.6.3 14 有“标识符列表仅声明函数参数的标识符。作为该函数定义一部分的函数声明符中的空列表指定该函数没有参数。” 矛盾。函数_定义_为 `int foo(){ return 0;}` 应该与 `int foo(void){ return 0;}` 相同。我怀疑使用的编译器不遵循此规范。 (3认同)
  • @tstanisl“每个参数的类型应与应用默认参数提升所产生的类型兼容。” - 来自标准 (6.7.6.3/15),关于与无原型函数类型的兼容性。此外,即使标准确实允许,这也正是警告应该捕获的类型。 (2认同)