查找未初始化成员变量的简单方法

Posted

技术标签:

【中文标题】查找未初始化成员变量的简单方法【英文标题】:Easy way find uninitialized member variables 【发布时间】:2011-01-07 04:00:37 【问题描述】:

我正在寻找一种简单的方法来查找未初始化的类成员变量。

runtimecompile time 中找到它们都可以。

目前我在类构造函数中有一个断点,并一一检查成员变量。

【问题讨论】:

好文章“寻找未初始化的类成员”-viva64.com/en/b/0354 如果使用 clang 编译器,您可以尝试内存清理器:clang.llvm.org/docs/MemorySanitizer.html。与 valgrind 相比,它执行动态检查并且开销显着减少。作者在 cppcon2014 youtube.com/watch?v=V2_80g0eOMc 上有很好的介绍 【参考方案1】:

-Wuninitialized ?

(这仅检查是否使用未初始化的变量,即是否

struct Q  
  int x, y;
  Q() : x(2) 
  int get_xy() const  return x*y; 
;

g++ 只会在用户调用get_xy() 而不分配给y 时发出警告。)

【讨论】:

还需要-O1或以上,不是默认的。 我无法让 g++ 4.3.3 警告带有 -Wuninitialized 的数据成员,你确定它在这里有效吗? (简单测试:将int main() return Q().get_xy(); 添加到您的代码中。) @Roger-plate:很遗憾,您需要使用int main() Q q; return q.get_xy(); 才能工作。【参考方案2】:

Valgrind 可以告诉你你是否在 linux 上。

【讨论】:

uname -a 还会告诉我我是否在 Linux 上.... 但这无济于事 ;-)【参考方案3】:

Valgrind(免费,在 Linux 上)和Purify(在 Windows 上)通过在特殊的虚拟机中运行您的代码来查找未初始化的变量、无效指针等。

这很容易使用并且非常强大;除了明显的未初始化变量之外,它可能会发现许多错误。

Coverity、Klocwork 和 Lint 可以使用静态代码分析找到未初始化的变量。

【讨论】:

“易于使用” 是主观的。使用 GCC 扩展汇编块,我们得到的只是一个指向块末尾的行号(结束括号),而不是导致问题的实际变量。即使--track-origins 也是如此。 @jww 这是一个 gcc 问题(没有产生足够的调试信息),而不是 valgrind 问题。如果您使用 -ggdb 或其他东西编译源代码,它可能会消失,但我会感到惊讶。我在 llvm 后端工作,那里的情况也差不多。 看来purify已经不是一个好的选择了:marlowa.blogspot.com.br/2015/08/the-death-of-purify.html【参考方案4】:

如果您使用的是 Visual Studio,您可以在调试模式下编译,在调试器中停止程序并查找哪些变量被初始化为包含 0xCC(堆栈)或 0xCD(堆)的字节。

尽管就我个人而言,我还是会投资一个静态分析工具以获得更彻底的方法。

【讨论】:

【参考方案5】:

/analyze on Visual Studio(“团队系统”)

【讨论】:

你确定这适用于未初始化的 member 变量吗?在我们的测试中,它只找到未初始化的局部变量。【参考方案6】:

如果您使用 GCC,您可以使用 -Weffc++ 标志,当变量未在成员初始化列表中初始化时会生成警告。这个:

class Foo

  int v;
  Foo() 
;

导致:

$ g++ -c -Weffc++ foo.cpp -o foo.o
foo.cpp: In constructor ‘Foo::Foo()’:
foo.cpp:4: warning: ‘Foo::v’ should be initialized in the member initialization list

一个缺点是-Weffc++ 也会在变量具有适当的默认构造函数时发出警告,因此不需要初始化。当您在构造函数中初始化变量时,它也会警告您,但不会在成员初始化列表中。它还会对许多其他 C++ 样式问题发出警告,例如缺少复制构造函数,因此当您想定期使用 -Weffc++ 时,您可能需要稍微清理一下代码。

还有一个错误导致它在使用匿名联合时总是给你一个警告,你目前无法解决这个问题,然后关闭警告,可以通过以下方式完成:

#pragma GCC diagnostic ignored "-Weffc++"

但总的来说,我发现 -Weffc++ 在发现许多常见的 C++ 错误方面非常有用。

【讨论】:

bug 还在吗? 我的编译器 (g++ 4.3.3) 只能检测到一部分未初始化的成员。检测不到非初始化简单类型数组:不产生警告,执行时不初始化 int 数组【参考方案7】:

cppcheck 会找到这个,例如:

cppcheck my_src_dir --output-file=check.txt --inconclusive --enable=warning

【讨论】:

可能是因为 cppcheck 没有那么聪明。它会警告不要在构造函数中初始化,但它通常无法检查复杂的路径,例如构造函数中的设置器初始化成员。 Cppcheck 从那时起得到了改进,1.75 版只能检测部分结构初始化。但是当然,像this one 这样的情况仍然对它来说太难了,尽管对人类来说也很难(正如我所观察到的)。 它仍然不理解委托构造函数。【参考方案8】:

考虑下面的代码

unint.cpp:

int main()

    int a;
    int b;
    a++;
    b = b + 5;

    return 0;

如果代码编译时带有以下注释,则会显示警告消息。

g++ -O3 -Wuninitialized unint.cpp

注意:-Wuninitialized 也需要 -O3 选项。

【讨论】:

输出:unint.cpp: In function 'int main()': unint.cpp:8: warning: 'a' is used uninitialized in this function unint.cpp:9: warning: ' b' 在此函数中未初始化使用 这个问题的标题是“轻松[to] find uninitialized member variables”。这些不是成员变量。我们都知道-Wunitialized 用于非成员变量,并且我们都应该已经拥有它作为已经使用-Wall -Wextra -Wpedantic 的一部分。此外,它不需要“-O3 选项”或任何其他形式的优化存在或不存在,尽管如果优化导致删除无副作用的变量,它们可能会影响返回的警告。【参考方案9】:

小心!这里提出的编译器选项既不可靠,也不独立于版本。考虑一个简单的例子:

class A 
  int a;
public:
  void mA() 
    printf("haha");
    ++a;
    int g = 2/a;
    printf("%i\n",g);
  
;

int main() 
  A a;
  a.mA();

使用g++ -O3 -Weffc++ -Wuninitialized 编译,这个东西报告uninitialized 的 gcc 版本高达 4.6,并在 4.7 和 4.8 上顺利通过(在 MacPorts 上测试)。然后,奇怪的是,如果我们删除 printf("haha");,4.7 和 4.8 都会突然看到 uninitialized A::aClang 稍微好一点,因为它以某种方式将垃圾(而不是方便的 0)分配给未初始化的变量,因此您更容易/更快地看到它们的灾难性影响。

我在发现上述未初始化的A::avalgrind 时运气不佳;也许建议valgrind 的绅士可以提供适当的选项来发现此错误。

底线:很好的问题,目前没有多少可靠的解决方案......(我的看法)。

【讨论】:

对于-O0 以上的任何优化级别,gcc 4.7 会优化除对printf 的调用之外的所有内容。我检查了汇编代码,它只是对printf 的两次调用。所以 gcc 的编译时评估没有检测到未初始化的值。而valgrind 没有机会在运行时检测到它,因为它只是两次使用常量参数调用 printf。【参考方案10】:

Visual Studio (MSVC) 有一个 /sdl(启用附加安全检查)编译器选项 (http://msdn.microsoft.com/en-us/library/jj161081.aspx)。在运行时,它:

执行类成员初始化。自动初始化类 对象实例化时指针类型为零的成员(在 构造函数运行)。这有助于防止使用未初始化的数据 与构造函数未显式关联的类成员 初始化。

这不会帮助您在编译时检测未初始化的成员变量,但它会使行为在运行时发生时更加可预测。当然,您不应该编写依赖于启用此选项的代码。

【讨论】:

【参考方案11】:

带有 clang-analyze 的 Clang 能够做到这一点。它将事件创建一个漂亮的 HTML 报告,指示何时访问未使用的变量。

【讨论】:

以上是关于查找未初始化成员变量的简单方法的主要内容,如果未能解决你的问题,请参考以下文章

宏定义中对结构体变量进行初始化,结构体成员变量前为啥加点号??

请问java构造函数如何初始化成员变量的啊?

java语言中成员方法可以初始化成员变量吗?

java 成员变量 静态成员变量 方法 静态方法初始化顺序

全局对象内的内置类型的成员变量是不是已初始化为零?

为什么在构造函数中初始化的成员变量会在离子角的ngInit中未定义?