【问题标题】:Function Prototype - Turning off argument checking函数原型 - 关闭参数检查
【发布时间】:2017-02-04 00:06:07
【问题描述】:

从关于 C 的 K&R Book 中,我收集到如果函数原型声明省略了参数(如在int foo(); 中),则类型和参数检查将被关闭,并且不会假设参数与旧版本的 C 兼容所以它不会破坏遗留代码。

但是下面的代码会抛出原型不匹配的编译错误:

#include <stdio.h>
void test();
int main(void) {
    test(34.5f);
}

void test(float a) {
    printf("%f\n", a);
}

错误:

C:\***.c:7:6: error: conflicting types for 'test'
 void test(float a) {
      ^

有什么解释吗?

【问题讨论】:

  • gcc 添加:note: an argument type that has a default promotion can't match an empty parameter name list declaration,所以void test(double a) 有效,但void test(float a) 无效。因此,“类型和参数检查已关闭,并且对参数没有任何假设”似乎过于简单化了。您应该参考 C 规范以了解该规则实际上是什么。
  • stackoverflow.com/questions/1630631/… 这个问题可能被一些人认为是那个问题的重复并结束了。
  • 编译器需要定义void test(double a) {},因为test(34.5f); 调用涉及默认参数提升,如果给定的声明不能用作原型,就会出现这种情况。其中一个促销活动是float ---&gt; double
  • @GrzegorzSzpetkowski 请注意,即使没有调用该函数,它仍然是一个错误

标签: c function function-prototypes


【解决方案1】:

当一个函数被多次声明时,所有声明必须具有兼容类型 (C11 6.2.7/2)。在您的代码中,f 被声明了两次 - 该定义也算作声明。

“兼容函数类型”的定义在C11 6.7.6.3/15:

对于要兼容的两种函数类型,两者都应指定兼容的返回类型。此外,参数类型列表(如果两者都存在)在参数数量和省略号终止符的使用方面应一致;相应的参数应具有兼容的类型。如果一种类型具有参数类型列表,而另一种类型由不属于函数定义的函数声明符指定且包含空标识符列表,则参数列表不应有省略号终止符和 每个参数都应与应用默认参数提升所产生的类型兼容。 如果一种类型具有参数类型列表,而另一种类型由包含(可能为空)标识符列表的函数定义指定,两者在参数数量上应一致,并且每个原型参数的类型应与将默认参数提升应用到相应标识符的类型所产生的类型兼容。 (在确定类型兼容性和复合类型时,每个声明为函数或数组类型的参数都被视为具有调整后的类型,每个声明为限定类型的参数被视为具有其声明类型的非限定版本。)

因此void test()void test(float) 不兼容。换句话说,在看到void test(); 之后,任何原型都只能使用默认参数提升未更改的类型。在这些促销活动下,float 更改为 double

我相信自第一个 C 标准以来一直如此。

【讨论】:

    【解决方案2】:

    使用double类型的参数定义函数。

    void test(double a) {
        //...
    }
    

    问题出在这个电话里

    test(34.5f);
    

    使用了将参数转换为双精度类型的默认参数提升。

    【讨论】:

    • 即使没有函数调用,代码仍然是违反约束的
    【解决方案3】:

    根据错误消息,具有默认提升的参数类型无法匹配空参数名称列表声明。所以问题是float会被提升为int,这会导致与函数定义的float参数不匹配。

    声明:

    void test();
    

    它告诉编译器存在一个函数test,它没有参数也没有返回值。

    定义:

    void test(float a)
    

    它告诉编译器 test() 实际上是什么,并且还提供了声明。

    【讨论】:

      【解决方案4】:

      所以,最后从这里给出的所有答案和一点点阅读中,这就是我学到的东西(我将它作为答案发布,以便对将来可能遇到这个问题的任何人有所帮助):

      • 当我调用 test(23.4f) 时 - 参数会自动“强制”为双精度参数,并将调用为 test(double),因为没有指定原型(至少对于参数)
      • 由于我使用任何参数进行的任何调用都将转换为“默认提升”,因此我不能将函数声明为test(float),因为它永远不会被使用/调用。所以,test(double) 有效,test(float) 无效。

      如果我错了,请纠正我,我会编辑我的答案。

      【讨论】:

        【解决方案5】:

        您告诉gcc 编译c11 代码,而不是作为K&R。

        我查看了-std= 选项,但没有一个特别有用。也许完全省略语言标准参数会有所帮助。或者指定为c89

        在 c11 中,始终需要完整的原型。所以第一次使用给函数参数=(void)。在 pre-c++ 中,这确实意味着“可能有或没有传递参数”。

        【讨论】:

        • “在 c11 中,始终需要完整的原型。”引用?
        • 不,尝试使用 c89。也没有使用它进行编译。
        • @Ryan:这不是 c11 的主要增强功能之一吗?或者我在想 c++11?
        • @wallyk:我认为您正在考虑 C++11,是的。我能找到的所有 C11 规范版本都将其描述为“过时”,但并未删除。
        猜你喜欢
        • 2021-05-29
        • 2012-08-04
        • 2016-02-08
        • 2022-01-05
        • 2011-11-24
        • 2021-12-09
        • 1970-01-01
        • 2012-02-08
        • 1970-01-01
        相关资源
        最近更新 更多