g++ 在不优化变量的情况下显示未初始化变量的警告

g++ Show warnings for uninitialized variables without optimizing out variables

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

#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
      |

问题所示,标志 -Og(或任何其他优化标志)是获得此警告所必需的。

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

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

可以看到,变量x已经被优化掉了。发生这种情况是因为 -Og 标志,如 问题中所述。

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

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

Is this possible with 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.

As you can see, the variable x has been optimized out.

这是 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 没有按应有的方式解释它。

P.S。 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++ 方面。