当编译器注意到具有未定义/未指定/实现定义行为的语句时,它不能发出警告(如果它抛出错误更好)吗?
可能要将语句标记为错误,标准应该这样说,但它至少可以警告编码器。实施这样的选择是否有任何技术困难?还是仅仅是不可能?
我得到这个问题的原因是,在a[i] = ++i;
达到序列点之前,在语句中是否知道代码正在尝试引用变量并在同一语句中修改它。
当编译器注意到具有未定义/未指定/实现定义行为的语句时,它不能发出警告(如果它抛出错误更好)吗?
可能要将语句标记为错误,标准应该这样说,但它至少可以警告编码器。实施这样的选择是否有任何技术困难?还是仅仅是不可能?
我得到这个问题的原因是,在a[i] = ++i;
达到序列点之前,在语句中是否知道代码正在尝试引用变量并在同一语句中修改它。
这一切都归结为
实施质量:警告越准确和有用,效果就越好。总是为每个程序打印:“该程序可能会或可能不会调用未定义的行为”然后编译它的编译器非常没用,但符合标准。值得庆幸的是,没有人编写像这样的编译器 :-)。
易于确定:编译器可能不容易确定未定义的行为、未指定的行为或实现定义的行为。假设您有一个 5 层深的调用堆栈,其中一个const char *
参数从顶层传递到链中的最后一个函数,最后一个函数调用printf()
将其const char *
作为第一个参数。你想让编译器检查它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。
不同的编译器会捕获不同的条件;大多数编译器都有警告级别选项,GCC 特别有很多,但是 -Wall -Werror 会打开大多数有用的选项,并强制它们出错。在 VC++ 中使用 \W4 \WX 进行类似的保护。
在 GCC 中,您可以使用 -ansi -pedantic,但它所说的是 pedantic,它会引发许多不相关的问题,并且很难使用很多第三方代码。
无论哪种方式,因为编译器捕获不同的错误,或者为相同的错误产生不同的消息,因此使用多个编译器很有用,不一定是为了部署,而是作为穷人的静态分析。C 代码的另一种方法是尝试将其编译为 C++。C++ 更强的类型检查通常会产生更好的 C 代码;但请确保如果您希望 C 编译正常工作,请不要专门使用 C++ 编译;您可能会介绍 C++ 特定的功能。同样,这不需要部署为 C++,而只是用作附加检查。
最后,编译器通常在性能和错误检查之间取得平衡。详尽地检查将花费许多开发人员无法接受的时间。出于这个原因,存在静态分析器,对于 C 有传统的 lint 和开源夹板。C++ 的静态分析更复杂,而且工具通常非常昂贵。我用过的最好的之一是 Programming Research 的 QAC++。我不知道任何享有盛誉的免费或开源 C++ 分析器。
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
不想这样做。-Wall
当您在语法上仍然正确的情况下做一些超出语言规范的事情时,GCC 会尽可能多地发出警告,但超出特定点时必须得到足够的通知。
您可以使用标志调用 GCC-Wall
以查看更多信息。
如果您的编译器不会对此发出警告,您可以尝试使用 Linter。
Splint 是免费的,但只检查 C http://www.splint.org/
Gimpel Lint 支持 C++,但价格为 389 美元——也许可以说服您的公司购买一份副本? http://www.gimpel.com/