首页 > 解决方案 > GCC - 没有关于带有 -O0 的未初始化数组的警告

问题描述

我的 GCC 7.3.0 和 8.2.0 有一些我无法解释的奇怪行为。该程序显然以分段错误结束:

int main()
{
    double array[2]={0, 0};
    printf("%f\n", array[999]);

    return 0;
}

编译

gcc -Wall -O2 main.c

产生警告

main.c: In function 'main':
main.c:6:5: warning: 'array[999]' is used uninitialized in this function [-Wuninitialized]
     printf("%f\n", array[999]);
     ^~~~~~~~~~~~~~~~~~~~~~~~~~

但是关闭优化:

gcc -Wall main.c

它根本不会产生任何警告。我的代码 linter 和调试编译 (gcc -g) 使用 -O0 并且没有发现我犯的类似的越界错误,直到我将其编译为启用了优化的发布。在 linter 中设置 -O1 会按预期发布警告。

标签: cgcc

解决方案


这是 GCC 中长期存在的、记录在案的限制。引用GCC 3.0 的手册

-Wuninitialized

如果在没有首先初始化的情况下使用自动变量,或者变量可能被setjmp调用破坏,则发出警告。

这些警告仅在优化编译时才可能出现,因为它们需要仅在优化时计算的数据流信息。如果您不指定-O,您根本不会收到这些警告。

当前版本的手册实际上已经删除了该引用的第二段,而是说“因为这些警告取决于优化,所以有警告的确切变量或元素取决于精确的优化选项和使用的 GCC 版本。” 这是因为,在 GCC 3.0(2001 年发布)和 GCC 8.2(2018 年发布)之间的某个时间点,编译器得到了改进,因此它在未优化时至少对未初始化变量的某些使用发出警告。例如,琐碎的测试

int foo(void) { int x; return x; }

使用 GCC 8.2 编译时确实会引发警告-O0 -Wall

值得指出的是,对未初始化变量的完美诊断会归结为臭名昭著的停机问题——这意味着它无法完成。您可以实现一组保守正确的规则(它们会检测所有未初始化变量的使用,但他们可能会声称某些变量在未初始化时未初始化使用),例如 Java 的明确赋值规则,但这种方法在历史上一直是在 C 程序员中不受欢迎。考虑到在不优化时对最小误报以及快速编译的需求,GCC 在优化时进行更精细分析的方法是可以理解的。


推荐阅读