【问题标题】:g++ Show warnings for uninitialized variables without optimizing out variablesg ++显示未初始化变量的警告而不优化变量
【发布时间】:2021-07-24 02:26:59
【问题描述】:

我有以下要调试的示例程序:

#include <iostream>
#include <assert.h>
using namespace std;

int f(int x) {

    assert(false); // something bad happens

    return 2 * x;
}

int main() {

    int a;

    for (int i = 0; i < 5; i++) {
        a++; // a is uninitialized
        cout << a << endl;
    }

    cout << f(1) << endl; 

}

这段代码有两个问题:

  1. 循环内的变量a未初始化
  2. 函数f导致程序崩溃

为了检测这些问题,我使用g++ -Wall -Og -g source.cpp 进行编译,并收到以下警告:

source.cpp: In function ‘int main()’:
source.cpp:17:10: warning: ‘a’ may be used uninitialized in this function [-Wmaybe-uninitialized]
   17 |         a++; // a is uninitialized
      |

正如this 问题所示,-Og 标志(或任何其他优化标志)对于获取此警告是必要的。

当我使用 gdb 调试生成的可执行文件时,它会崩溃(因为 assert 语句)并且回溯看起来像这样:

[...]
#4  0x000055555555528f in f (x=<optimized out>) at source.cpp:7
#5  0x0000555555555322 in main () at source.cpp:21

如您所见,变量x 已被优化。发生这种情况是因为 -Og 标志,如 this 问题中所述。

显然,我不希望将其用于调试目的。但是当我删除-Og 标志时,前面提到的警告将不再出现。我现在想找到一种方法来获得此警告,而无需优化变量。 g++ 可以做到这一点吗?

我在 Ubuntu 20.10 上使用 g++ 版本 10.2.0 和 gdb 版本 9.2。

【问题讨论】:

  • 嗯,我不使用-Og here,但我仍然收到警告。
  • @NathanOliver 是的,我搞砸了,使用了错误的代码。我编辑了我的帖子。现在它应该是正确的。
  • 因为检测未初始化的变量需要做程序流分析(查看从声明到第一次使用的每一个可能的代码路径),这很昂贵。许多编译器将此检测推迟到优化阶段,出于优化原因,无论如何都会进行此类分析。
  • @largest_prime_is_463035818 这看起来很有趣。 -Wuninitialized=verbose 标志会非常有用。

标签: c++ gdb g++ gcc-warning compiler-flags


【解决方案1】:

g++ 可以做到这一点吗?

没有。不幸的是,GCC 执行“使用的值是否未初始化?”在其优化通道之一中进行分析,并且禁用优化也会禁用该通道。

将此与 Clang/LLVM 进行对比,后者的明确目标是使警告依赖于优化。

clang++ -Wall -Wextra -c t.cc  # no optimization

t.cc:17:9: warning: variable 'a' is uninitialized when used here [-Wuninitialized]
        a++; // a is uninitialized
        ^
t.cc:14:10: note: initialize the variable 'a' to silence this warning
    int a;
         ^
          = 0
1 warning generated.

如您所见,变量 x 已被优化。

这是g++(如果它没有发出参数的位置信息)或gdb(如果它没有解码g++ 发出的信息)的缺陷。

理论上-Og 应该降低您的调试体验,尽管它显然在这里做了。

查看readelf -wig++ 编译代码,我明白了:

<1><285d>: Abbrev Number: 114 (DW_TAG_subprogram)
    <285e>   DW_AT_external    : 1
    <285e>   DW_AT_name        : f
    <2860>   DW_AT_decl_file   : 1
    <2861>   DW_AT_decl_line   : 5
    <2862>   DW_AT_decl_column : 5
    <2863>   DW_AT_linkage_name: (indirect string, offset: 0x169d): _Z1fi
    <2867>   DW_AT_type        : <0xe4f>
    <286b>   DW_AT_low_pc      : 0x3d
    <2873>   DW_AT_high_pc     : 0x23
    <287b>   DW_AT_frame_base  : 1 byte block: 9c       (DW_OP_call_frame_cfa)
    <287d>   DW_AT_GNU_all_call_sites: 1
    <287d>   DW_AT_sibling     : <0x28e1>
 <2><2881>: Abbrev Number: 115 (DW_TAG_formal_parameter)
    <2882>   DW_AT_name        : x
    <2884>   DW_AT_decl_file   : 1
    <2885>   DW_AT_decl_line   : 5
    <2886>   DW_AT_decl_column : 11
    <2887>   DW_AT_type        : <0xe4f>
    <288b>   DW_AT_location    : 0x2dc (location list)
    <288f>   DW_AT_GNU_locviews: 0x2d8
...

我没有看到0x2dc0x2d8 的任何定义,所以在我看来这是g++ 方面的问题,但我不完全确定如何阅读位置列表。 g++ 可能确实发出了所需的信息,而 GDB 没有按应有的方式解释它。

附: lldb 显示与 GDB 相同的输出:

a.out: t.cc:7: int f(int): Assertion `false' failed.
Process 974666 stopped
* thread #1, name = 'a.out', stop reason = hit program assert
    frame #4: 0x0000555555555205 a.out`f(x=<unavailable>) at t.cc:7:5
   4
   5    int f(int x) {
   6
-> 7        assert(false); // something bad happens
   8
   9        return 2 * x;
   10   }

这额外表明问题可能出在g++ 方面。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-06
    • 1970-01-01
    • 2017-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-07
    相关资源
    最近更新 更多