【问题标题】:How to turn "implicit declaration" warnings in $CC into errors?如何将 $CC 中的“隐式声明”警告变成错误?
【发布时间】:2011-10-11 10:17:51
【问题描述】:

序言:我的 C 可能相当生锈;我在 1993 年左右开始编写 C 程序——当时的编译器可能有所不同,但我记得当尝试引用未声明的 C 函数时,编译器会中止。这是凭记忆。

目前,当我 [故意] 不匹配或省略下面的 bar 的声明(其定义在 bar.c 中)时,我很困惑为什么 GCC (4.4.3) 对我如此宽容。因为编译器没有警告我,所以程序在运行时会出现致命的寻址错误——因为bar 想要一个地址并且给定了一个整数,所以它最终将该整数取消引用为地址。

一个严格的编译器,或者我认为,会因错误而中止我。我错过了什么吗?我的构建命令行如下:

cc -o foobar -g -Wall -std=c99 -fexec-charset=ISO-8859-1 -DDEBUG foo.c bar.c

foo.c:

int main() {
    int a;
    bar(a);
    return 0;
}

bar.c:

void bar(int * a) {
    *a = 1;
}

我故意省略了bar 的声明,并且如前所述,故意将其传递给它一个整数(实际上可能是任何东西),而不是它的实际定义否则会要求的地址。因为$(CC) 并没有阻止我,所以我最终遇到了分段错误(x86,Ubuntu 10.04)。我知道一个兼容的 C(C99?)编译器会隐式地为 bar 创建一个 int bar(void) 声明,如果没有找到其他方法,但在这种情况下,这显然不是我想要的!

我想保护自己免受此类错误的影响——我犯了不匹配声明和定义或完全忽略前者的人为错误。

我尝试在没有链接步骤的情况下调用编译器(使用-c 开关)——但这并不重要,因为编译仍然会成功并出现警告。虽然链接器可能会抱怨,但我希望编译器在发生这种情况之前阻止我

实际上想把我所有的警告变成错误(例如-Werror),因为:

  • 我本可以在foo.c 的顶部包含错误的float bar(double a);,这将完全消除警告,但不会改变结果程序崩溃的事实;唉,这个程序编译成功而没有警告(即使使用-Wall 开关)但仍然有问题
  • 我已经并且将会有其他类型的警告,这些警告应该保持警告而不妨碍成功构建程序
  • 它将处理问题的影响,而不是问题本身
  • 这不仅仅是警告的类型,还有它的特定实例;我不想将特定警告变成错误,因为在某些情况下不适用;如果解决方案没有考虑到警告发生的具体情况和上下文,这将过于“粗略”

【问题讨论】:

  • 不回答这个问题,但是使用 C++ 编译器(隐式声明是完全禁止的)可以解决所有问题。 (可能仅在不匹配的声明/定义的链接时,但仍被捕获。)
  • @Mat 是的,我知道这一点。感谢您的提醒。不能在这里使用 C++。我确实解决了这个问题,但会等待一些替代方案,然后发布我自己的答案。

标签: c gcc


【解决方案1】:

要在使用 gcc 编译时将此警告变为错误,请将开关 -Werror=implicit-function-declaration 传递给编译器。

尝试回答您的“为什么”问题:是的,默认情况下这是警告而不是错误,这可能看起来很奇怪。这是出于历史原因。有关详细信息,请参见例如Why does/did C allow implicit function and typeless variable declarations?,或在http://cm.bell-labs.com/who/dmr/chist.html 阅读里奇自己的话。

【讨论】:

  • 谢谢@Carl。不幸的是,我已经很长时间没有偶然发现特定的用例了。我希望将来能够重新审视它,因为它处于如此基础的水平。这就是说,我不再认为-combine 是一个好的解决方案,实际上我认为避免问题弊大于利。根本问题是编译器正确地帮助程序员,避免程序员不得不支持编译器的情况。
【解决方案2】:

您可能会为 gcc 强制附加警告:

-Wmissing-prototypes
-Wmissing-declarations

同时使用(连同 -Werror)可能会帮助您避免这种情况,但需要编写更多代码。

灵感来自this

编辑:示例

// file: mis1.c
int main(void)
{
    int a;

    bar(a);

    return 0;
}

// file: mis2.c
#include <stdio.h>

double bar(double a)
{
    printf("%g\n", a);
    return a;
}

使用 gcc 3.3.4 (DJGPP) 编译为:

gcc -Wall -Wmissing-prototypes -Wmissing-declarations -Werror mis2.c mis1.c -o mis.exe

编译器输出:

mis2.c:5: warning: no previous prototype for `bar'
mis1.c: In function `main':
mis1.c:6: warning: implicit declaration of function `bar'

修复? #在两个文件中包含以下文件:

// file: mis.h
extern int bar(int);

重新编译得到:

mis2.c:6: error: conflicting types for `bar'
mis.h:3: error: previous declaration of `bar'

修复?用同样的方式定义和声明bar,正确的,例如mis.h:

// file: mis.h
extern double bar(double);

同样,您可以更改 mis2.c 中的 bar() 以匹配 mis.h。

【讨论】:

  • 谢谢 - 非常有用的信息,特别是如果有人认为可以使用 -Werror=missing-prototypes-Werror=missing-declarations 并得到看起来像我所追求的东西,除了一个非常重要的事实:我有一个虚假的说法,foo.c 中的double bar(double a); 完全未被您的提议检测到,bar 再次崩溃程序。
  • @amn:我刚刚添加的实际示例怎么样?
  • 这是一个改进,但最初的问题仍然存在 - 有一个“错误的”原型 bar 并且有一个调用(根据原型)编译得很好但自然地,程序崩溃了.这要么是 C 的一个大洞,要么我需要纠正我在做什么/预期错了。顺便添加了自己的答案。
【解决方案3】:

来自the gcc docs on warnings

-Wimplicit-function-declaration(仅限 C 和 Objective-C) 在声明之前使用函数时发出警告。在 C99 模式(-std=c99 或 -std=gnu99)下,默认情况下会启用此警告,并通过 -pedantic-errors 将其变为错误。此警告也由 -Wall 启用。

...

-pedantic-errors(我的重点)与 -pedantic 类似,只是产生错误而不是警告。

...

-迂腐的 发出严格的 ISO C 和 ISO C++ 要求的所有警告;拒绝所有使用禁止扩展的程序,以及其他一些不遵循 ISO C 和 ISO C++ 的程序。对于 ISO C,遵循使用的任何 -std 选项指定的 ISO C 标准版本。

在我看来 -pedantic-errors 会做你想做的事(将这些警告变成错误),但听起来它也会打开你可能想要或不想要的许多其他检查。 =/

【讨论】:

    【解决方案4】:

    我发现最接近我的问题的解决方案是简单地使用标志-combine,这会间接导致编译器在尝试调用缺少原型或原型不匹配或不匹配的函数时中止编译定义。

    我担心它有缺点。由于输入文件现在在一次编译运行中组合在一起,因此最终得到一个目标文件,这有其自身的一些含义。简而言之,-combine 所做的不仅仅是解决我的问题,而这本身可能就是一个问题。

    【讨论】:

      【解决方案5】:

      您可以将所有警告变为错误

      cc [..] -Werror [..]
      

      。这将部分解决您的问题。

      【讨论】:

      • 除了我已经说过的-Werror,它并没有真正解决我的问题。我本可以在foo.c 的顶部包含一个完全不正确的float bar(double a); 声明,并且根本不会生成任何警告!生成的程序仍然会崩溃,因为实际的 bar 仍然需要一个地址。
      【解决方案6】:
      • 我可能包含了错误的浮动条(双 a);在 foo.c 的顶部,它完全消除了警告,但不会改变
        结果程序崩溃的事实。唉,一个程序
        编译成功且没有警告(即使使用 -Wall 开关)
        并在运行时完美崩溃。

      因此,除了其他措施之外,还必须在foo.cbar.c 中包含相同的头文件(包括原型)。这可以确保在两个地方都应用正确的原型。

      【讨论】:

      • 它没有解决我所描述的情况中的明显问题 - 即人类可能会犯错误并且不匹配两者的组合 - 声明,定义和/或原型(总共分布在几个翻译单元中),在编译过程中甚至不会弹出警告,但会产生一个“完美”的错误程序。相反,您指的是程序员已经通过提供适当的声明自己修复了错误的场景。正如@Mat 所说,C++ 已经承认并解决了实际问题。
      猜你喜欢
      • 2015-08-21
      • 2011-02-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-16
      相关资源
      最近更新 更多