未定义/未指定/实现定义的行为警告?

时间:2010-02-20 06:56:03

标签: c++ c compiler-warnings undefined unspecified

当注意到具有未定义/未指定/实现定义的行为的语句时,编译器是否不能发出警告(如果它抛出错误会更好)?

可能将语句标记为错误,标准应该这样说,但它至少可以警告编码器。实施这种方案有任何技术困难吗?或者它只是不可能?

我得到这个问题的原因是,在a[i] = ++i;之类的语句中,在达到序列点之前,不会知道代码是在尝试引用变量并在同一语句中修改它。

6 个答案:

答案 0 :(得分:7)

这一切归结为

  • 实施质量:警告越准确越有用,越好。一个总是打印的编译器:“这个程序可能会也可能不会为每个程序调用未定义的行为”,然后编译它,这是非常没用的,但是符合标准。值得庆幸的是,没有人编写这样的编译器: - )。

  • 易于确定:编译器可能无法轻松确定未定义的行为,未指定的行为或实现定义的行为。假设您有一个深度为5级的调用堆栈,其中const char *参数从顶层传递到链中的最后一个函数,最后一个函数使用printf()调用const char * 1}}作为第一个参数。您是否希望编译器检查const char *以确保它是正确的? (假设第一个函数对该值使用了一个文字字符串。)从文件中读取const char *的时间如何,但是您知道该文件将始终包含正在打印的值的有效格式说明符?

  • 成功率:编译器可能能够检测到许多可能未定义,未指定等的构造;但是“成功率”非常低。在这种情况下,用户不希望看到大量“可能未定义”的消息 - 过多的虚假警告消息可能隐藏真实的警告消息,或者提示用户在“低警告”设置下编译。那很糟糕。

对于您的特定示例,gcc会发出有关“可能未定义”的警告。它甚至警告printf()格式不匹配。

但是,如果您希望编译器为所有未定义/未指定的案例发布诊断,则不清楚是否应该/可以工作。

假设您有以下内容:

#include <stdio.h>
void add_to(int *a, int *b)
{
    *a = ++*b;
}

int main(void)
{
    int i = 42;
    add_to(&i, &i); /* bad */
    printf("%d\n", i);
    return 0;
}

编译器是否应警告*a = ++*b;行?

正如gf在评论中所说,编译器无法跨翻译单元检查未定义的行为。经典示例将变量声明为一个文件中的指针,并将其定义为另一个文件中的数组,请参阅comp.lang.c FAQ 6.1

答案 1 :(得分:2)

gcc在这种情况下会发出警告(至少使用-Wall):

#include <stdio.h>

int main(int argc, char *argv[])
{
  int a[5];
  int i = 0;

  a[i] = ++i;

  printf("%d\n", a[0]);

  return 0;
}

给出:

$ make
gcc -Wall main.c -o app
main.c: In function ‘main’:
main.c:8: warning: operation on ‘i’ may be undefined

编辑:

如果您出于某种原因不希望-Wsequence-point,则快速阅读man page会显示-Wall会执行此操作。

答案 2 :(得分:2)

不同的编译器会捕获不同的条件;大多数编译器都有警告级别选项,GCC特别有很多,但是-Wall -Werror会打开大多数有用的,并强制它们出错。在VC ++中使用\ W4 \ WX进行类似的保护。

在海湾合作委员会你可以使用-ansi -pedantic,但迂腐是它所说的,并且会引发许多无关紧要的问题,并且很难使用很多第三方代码。

无论哪种方式,因为编译器捕获不同的错误,或者为同一错误产生不同的消息,因此使用多个编译器是有用的,不一定是用于部署,而是作为穷人的静态分析。 C代码的另一种方法是尝试将其编译为C ++; C ++的强类型检查通常会产生更好的C代码;但是请确保如果您希望C编译工作,请不要仅使用C ++编译;您可能会介绍C ++特定的功能。同样,这不需要部署为C ++,而只是用作额外的检查。

最后,编译器通常在性能和错误检查之间取得平衡;要彻底检查会花费很多开发人员不会接受的时间。出于这个原因,存在静态分析仪,对于C,存在传统的棉绒和开源夹板。静态分析C ++更复杂,而且工具通常非常昂贵。我使用过的最好的一个是来自Programming Research的QAC ++。我不知道任何声誉的任何免费或开源C ++分析器。

答案 3 :(得分:1)

相反,编译器需要对未定义的行为进行任何类型的诊断:

  

§1.4.1:
  可诊断规则集包含本国际标准中的所有语法规则和语义规则,但那些包含“无需诊断”或被描述为导致“未定义行为”的明确表示法的规则除外。

强调我的。虽然我同意它可能很好,但编译器在尝试符合标准方面有足够的问题,更不用说教程序员如何编程了。

答案 4 :(得分:0)

当你在语法规范方面做一些事情时,GCC会尽可能地发出警告,同时仍然在句法上是正确的,但超出某一点必须足够的信息。

您可以使用-Wall标记调用GCC以查看更多内容。

答案 5 :(得分:0)

如果您的编译器不会对此发出警告,您可以尝试使用Linter。

Splint是免费的,但只检查C http://www.splint.org/

Gimpel Lint支持C ++但价格为389美元 - 也许你的公司可以说服购买一份副本? http://www.gimpel.com/