我的GCC 7.3.0和8.2.0有一些无法解释的奇怪行为。该程序显然以Segmentation错误结束:
int main()
{
double array[2]={0, 0};
printf("%f\n", array[999]);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
编译与
gcc -Wall -O2 main.c
Run Code Online (Sandbox Code Playgroud)
产生警告
main.c: In function 'main':
main.c:6:5: warning: 'array[999]' is used uninitialized in this function [-Wuninitialized]
printf("%f\n", array[999]);
^~~~~~~~~~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)
但是在关闭优化的情况下:
gcc -Wall main.c
Run Code Online (Sandbox Code Playgroud)
它根本不发出警告。我的Code linter和Debug编译(gcc -g)使用-O0,并且没有遇到类似的超出范围的错误,直到我将其编译为“打开并启用优化”。在短绒棉衣中设置-O1将按预期发布警告。
这是GCC中长期存在的记录限制。引用GCC 3.0手册:
-Wuninitialized如果未使用自动变量而没有对其进行初始化,或者如果某个变量可能被
setjmp调用破坏,则发出警告。这些警告仅在优化编译时才可能出现,因为它们需要仅在优化时才计算的数据流信息。如果不指定
-O,则不会收到这些警告。
该手册的当前版本实际上已删除了该引用的第二段,而是说:“由于这些警告取决于优化,因此存在警告的确切变量或元素取决于所使用的精确优化选项和GCC版本。” 这是因为,在GCC 3.0(2001年发布)和GCC 8.2(2018年发布)之间的某个时刻,对编译器进行了改进,以便在未优化时至少会对未初始化变量的某些使用发出警告。例如,琐碎的测试
int foo(void) { int x; return x; }
Run Code Online (Sandbox Code Playgroud)
当使用GCC 8.2进行编译时,确实会引发警告-O0 -Wall。
值得指出的是,对未初始化变量的完美诊断可以减少臭名昭著的“ 停止问题”,这意味着它无法完成。您可以实施一组保守地正确的规则(它们将检测未初始化变量的所有使用,但它们可能声称某些变量在未使用时未初始化),例如Java的确定赋值规则,但这种方法历来是在C程序员中不受欢迎。考虑到需要最小的误报,同时在不进行优化时也需要快速编译的要求,因此可以理解,GCC在优化启动时进行更精细分析的方法是可以理解的。