GCC -Wuninitialized / -Wmaybe-uninitialized 问题

Posted

技术标签:

【中文标题】GCC -Wuninitialized / -Wmaybe-uninitialized 问题【英文标题】:GCC -Wuninitialized / -Wmaybe-uninitialized issues 【发布时间】:2013-01-03 04:04:36 【问题描述】:

我在使用gcc-4.7 (Ubuntu/Linaro 4.7.2-11precise2) 4.7.2 时遇到了一个非常奇怪的问题。我无法在没有警告的情况下编译以下有效代码:

extern void dostuff(void);

int test(int arg1, int arg2)

    int ret;

    if (arg1) ret = arg2 ? 1 : 2;

    dostuff();

    if (arg1) return ret;

    return 0;

编译选项和输出:

$ gcc-4.7 -o test.o -c -Os test.c -Wall
test.c: In function ‘test’:
test.c:5:6: warning: ‘ret’ may be used uninitialized in this function [-Wmaybe-uninitialized]

但是,以下代码编译时没有警告(尽管汇编效率稍低):

extern void dostuff(void);

int test(int arg1, int arg2)

    int ret;

    if (arg1 && arg2) ret = 1;
    if (arg1 && !arg2) ret = 2;

    dostuff();

    if (arg1) return ret;

    return 0;

我有点卡住了,正在考虑这是一个编译器错误。有什么想法吗?

【问题讨论】:

你的意思可能是ret == arg2 ? 1 : 2;?? 不,语法没问题。我的意思是如果arg1=0, arg2=0返回0,如果arg1=1, arg2=1返回1,如果arg1=1, arg2=0返回2。这个 sn-p 是我遇到的一个更大问题的简化案例。 制作 ret volatile 也可以解决问题,但并不理想。 int ret; 并不意味着ret == 0,如果您更改int ret = 0;,那应该可以解决您的问题并正确地符合您的逻辑。您目前拥有的代码不会将 ret 初始化为 0.ret 具有 Indeterminate 值,因为它是未显式初始化的本地/自动变量。但这并不能回答潜在的异常。 感谢 Alok,但这个构建的示例可能会产生错误的解决方案。代码密度在这里非常重要,在实际函数中ret 是一个大数组,如果不使用我不想初始化它。查看我的第一个程序,ret 确实从未使用过未初始化,所以警告是不正确的,不是吗? 【参考方案1】:

这确实是 gcc 中的一个已知问题。 gcc 因举报 incorrect uninitialized variables 而臭名昭著。 不足之处已及时指出,有克服不足的主动权:Better Uninitialized Warnings:

GNU 编译器集合通过选项-Wuninitialized 警告使用未初始化的变量。然而,当前的实现有一些明显的缺点。一方面,一些用户想要更详细和一致的警告。另一方面,一些用户希望得到尽可能少的警告。该项目的目标是实现这两种可能性,同时提高当前的能力。

该计划旨在提供更好的警告,并引用了与您的案例类似的示例案例。相关部分是:

对于特定用户而言,用户所理解的误报可能会有所不同。一些用户对由于优化器的操作与当前环境相结合而隐藏的案例感兴趣。但是,许多用户不是,因为这种情况是隐藏的,因为它不会出现在编译的代码中。典型的例子是

int x;
if (f ())
     x = 3;
return x;

对于当前环境,'f' 总是返回非零值,因此,它可能会被优化掉。在这里,一组用户希望得到一个未初始化的警告,因为 'f' 在别处编译时可能返回零。然而,其他用户组会认为虚假警告是关于正在编译的可执行文件中不会出现的情况。

【讨论】:

一方面,一些用户喜欢-pedantic-Werror。所以这些用户不得不为gcc 4、5、6、7关闭Wuninitialized。2018年还是坏掉了。【参考方案2】:

不确定gcc 在此期间是否已修复。如果没有,您可能想尝试clang。恕我直言,它是更好的编译器,并且可以进行更好的代码分析。

仅仅因为某些 cmets 声称编译器是正确的,ret 可能会在未初始化的情况下使用,这是相反的证明。代码

int test(int arg1, int arg2)

    int ret;
    if (arg1) ret = arg2 ? 1 : 2;
    dostuff();
    if (arg1) return ret;
    return 0;

只需将两个相同的if 语句合并为一个,即可轻松转换为以下代码:

int test(int arg1, int arg2)

    if (arg1) 
        int ret = arg2 ? 1 : 2;
        dostuff();
        return ret;
    
    dostuff();
    return 0;

这是等效的代码,现在应该很明显,ret 永远不能在未初始化的情况下使用。编译器错误,警告没有意义。

不过话说回来,代码还可以进一步简化:

int test(int arg1, int arg2)

    dostuff();
    return (arg1 ? (arg2 ? 1 : 2) : 0);

问题解决了,ret 不见了。

【讨论】:

以上是关于GCC -Wuninitialized / -Wmaybe-uninitialized 问题的主要内容,如果未能解决你的问题,请参考以下文章

Mac OSX 上的 GCC——多个版本的 gcc

mingw和gcc的关系?mingw具有gcc的全部核心编译功能吗

gcc 和 g++/gcc-c++ 有啥区别?

ubuntu系统下怎么安装gcc编译器

GCC:在 GCC 版本之间伪装

arm gcc 内嵌汇编,gcc该是啥选项呢