在 C 语言中应该处理多紧急的警告?

How urgent should warnings be treated in C?

我不知道这个习惯是从哪里来的,但通常我会将编译器警告视为错误。我知道该程序仍然 运行 但警告困扰着我。我认为对我来说毫无意义的是

Narrowing conversion from <data type> to <data type> is implementation-defined.

尽管如此,我仍在尝试修复它。所以我来问一下,编译器警告应该多紧急处理?我很确定其中很多会有所不同,所以有人可以告诉我要注意哪些吗?

由于历史悠久,C 编译器往往会针对许多在任何其他语言中被视为错误的问题发出 警告 。 C 标准在 C11/C18 5.1.1.3:

中明确提到这是一个可能的结果

5.1.1.3 Diagnostics

  1. A conforming implementation shall produce at least one diagnostic message (identified in an implementation-defined manner) if a preprocessing translation unit or translation unit contains a violation of any syntax rule or constraint, even if the behavior is also explicitly specified as undefined or implementation-defined. Diagnostic messages need not be produced in other circumstances. 9)

  2. EXAMPLE An implementation shall issue a diagnostic for the translation unit:

      char i;
      int i;
    

    because in those cases where wording in this International Standard describes the behavior for a construct as being both a constraint error and resulting in undefined behavior, the constraint error shall be diagnosed.

Footnotes

  1. The intent is that an implementation should identify the nature of, and where possible localize, each violation. Of course, an implementation is free to produce any number of diagnostics as long as a valid program is still correctly translated. It may also successfully translate an invalid program.

(强调我的)

因此,一个程序成功编译这一事实并不能证明它在 C 中没有做一些严重错误的事情。例如,许多未定义的行为往往会根据命令行改变同一编译器的行为开关。

C 编译器仍然非常保守,任何默认发出的警告都应该认真对待并彻底调查。

并非所有诊断都由默认值启用。例如 GCC 有两个广泛的 category switches:

-Wall

This enables all the warnings about constructions that some users consider questionable, and that are easy to avoid (or modify to prevent the warning), even in conjunction with macros. This also enables some language-specific warnings described in C++ Dialect Options and Objective-C and Objective-C++

-Wextra

This enables some extra warning flags that are not enabled by -Wall.

As -Wall 提到它诊断出的有问题的结构很容易避免或修改。 -Wextra 为更多风格问题添加诊断,例如“未使用的变量”,有时会突出显示代码中的实际问题 - 也许您应该使用 that 变量 j 但在代码中打错了,或者忘记乘以它...

我的主观但专家意见是,如果

  • 即使没有 -Wall -Wextra 或特定的警告选项,编译器也会发出警告,我什至不会 运行 它用于调试目的。

  • 如果不分析 -Wall -Wextra 产生的每个警告,我不会将代码投入生产使用。当然,如果您解决这些问题,这个过程会更容易,因为诊断消息的数量会减少。


Narrowing conversion from to is implementation-defined.

默认情况下很可能未启用此诊断消息。它告诉我们像

这样的操作
int i;
float f = i;

已尝试,转换的行为因实现而异。通常是实现方式(编译器、操作系统、处理器)——因此,如果您查阅了编译器手册并发现那里记录的实现定义的行为是可以接受的,那么以某种方式抑制它当然是可以接受的。

如果现在您的目标是相同的操作系统,使用相同的编译器或具有相似行为的编译器 (GCC/Clang) 和相同的处理器系列,那么最好只抑制这些诊断实现定义的 行为与命令行开关而不是在代码中添加 casts 来抑制可能的可移植性问题。这样,如果您需要审查这些以移植到新的实施,您可以重新启用警告。


另一方面,有关于未定义行为的警告信号:

x.c: In function ‘main’:
x.c:5:22: warning: right shift count >= width of type
        [-Wshift-count-overflow]
    5 |     printf("%d\n", a >> 33);
      |                      ^~

x.c: warning: iteration 2147483647 invokes undefined behavior
        [-Waggressive-loop-optimizations]
    8 |     for (i = 0; i >= 0; i++) { }
      |                         ~^~

(只有在启用优化时才会被 GCC 诊断)

x.c:5:14: warning: format ‘%f’ expects argument of type ‘double’,
      but argument 2 has type ‘int’ [-Wformat=]
5 |     printf("%f\n", i);
  |             ~^     ~
  |              |     |
  |              |     int
  |              double
  |             %d

这些确实说明了严重的编码错误,其影响可能真的很难调试 - 即使在相同的编译可执行文件中,相同结构的行为也可能不同,更不用说不同的处理器了。就个人而言,在我修复这些问题之前,即使出于测试目的,我什至都不会费心 运行 该程序。