4

如果您使用过 Memcheck(来自 Valgrind),您可能会熟悉此消息...

条件跳转或移动取决于未初始化的值

我已经读过这个,当你使用一个未初始化的值时它就会发生。

MyClass s;
s.DoStuff();

这会起作用,因为s它是自动初始化的……所以如果是这样的话,而且它起作用了,为什么 Memcheck 告诉我它没有初始化?是否应该忽略该消息?

也许我误解了错误指向我的地方。从 Valgrind 手册中,实际的错误片段是......

int main()
{
  int x;
  printf ("x = %d\n", x);
}

但是,在我的代码中,我看不到类似的东西。然而,我注意到堆栈跟踪 Memcheck 顶部的函数显示我是一个虚函数;这可能与它有关吗?

==14446== Conditional jump or move depends on uninitialised value(s)
==14446==    at 0x414164: vimrid::glut::GlutApplication::FinishRender() (GlutApplication.cpp:120)
==14446==    by 0x422434: vimrid::demos::filterdemos::FilterDemo3::Render() (FilterDemo3.cpp:260)
==14446==    by 0x412D3D: vimrid::VimridApplication::UpdateAndRender() (VimridApplication.cpp:93)
==14446==    by 0x4144BA: vimrid::glut::GlutApplication::glutHandleDisplay() (GlutApplication.cpp:201)
==14446==    by 0x41486A: vimrid::glut::GlutApplication::glutCallbackDisplay() (GlutApplication.cpp:277)
==14446==    by 0x54D9FAA: (within /usr/lib64/libglut.so.3.8.0)
==14446==    by 0x54DDA4A: fgEnumWindows (in /usr/lib64/libglut.so.3.8.0)
==14446==    by 0x54DA4A3: glutMainLoopEvent (in /usr/lib64/libglut.so.3.8.0)
==14446==    by 0x54DAEB5: glutMainLoop (in /usr/lib64/libglut.so.3.8.0)
==14446==    by 0x413FF8: vimrid::glut::GlutApplication::Run() (GlutApplication.cpp:112)
==14446==    by 0x41249D: vimrid::Launcher::runDemo(vimrid::VimridSettings&) (Launcher.cpp:150)
==14446==    by 0x412767: vimrid::Launcher::Launch(int, char**) (Launcher.cpp:62)

更新1:

我查看了 GlutApplication.cpp:120,看起来未初始化的变量正在传递给该行的函数。简单的!

4

6 回答 6

17

您可以将标志添加--track-origins=yes到 valgrind,它将为您提供有关未初始化数据来源的信息。它运行速度较慢,但​​可能会有所帮助。

资料来源:Valgrind 用户手册

于 2011-04-15T18:19:54.823 回答
6

您可以发布更完整的示例吗?如果没有某种形式的 goto 或 flow 更改语句,很难看出如何会出现特定错误。

我最常在如下代码中看到此错误

MyClass s1;
...
if ( someCondition ) { 
  goto Foo:
}
MyClass s2;
Foo:
cout << s2.GetName();

这段代码基本上是不正确的。原因是即使 s2 有一个构造函数,如果 someCondition 为真,它也不会执行。goto 语句将跳过初始化,并且在程序的最后一行 s2 将未初始化并基本上指向垃圾。

编辑

您可能还想查看此页面,该页面提供了有关如何破译此特定 valgrind 错误的提示

https://computing.llnl.gov/code/memcheck/#deciphering4

附录

我刚刚发现的另一个常见原因是,当您将一些整数常量传递给可变参数函数时,它们作为整数放入堆栈,但是当被调用者将它作为长整数时,您在 64 上遇到了问题-位机。

我几乎要放弃并认为 valgrind 很愚蠢,然后我意识到只需将其转换为 long 即可修复它。

所以我的结果是:认真对待这些信息。

于 2009-04-19T19:13:20.907 回答
3

如果 Valgrind 声明一个值没有被初始化,那么在 99.5% 的情况下它真的没有被初始化。通常,当编译器报告使用未初始化的值(GCC 中的 -Wuninitialized)时,您会检查内联展开,因为可以声明(而不是初始化)未初始化的值,例如 10 级内联函数“调用”(或模板展开)高于实际的 GCC 报告。Valgrind 做同样的事情,但在运行时。因此,您应该检查未初始化值从声明(而不是初始化)的地方到实际使用它的地方的整个路径。例如,路径可以是:函数调用的级联,其中每个函数将其参数(可能还有未初始化的值)传递给下一个函数。当实际使用该值时,Valgrind 将在最后一个函数中报告。

通常你不应该忽略 Valgrind 所说的。Valgrind 不是一个简单的跟踪程序。可以看成是一个虚拟机:

Valgrind 本质上是一个使用即时 (JIT) 编译技术的虚拟机,包括动态重新编译。原始程序中的任何内容都不会直接在主机处理器上运行。相反,Valgrind 首先将程序转换为一种临时的、更简单的形式,称为中间表示 (IR),它是一种与处理器无关的、基于 SSA 的形式。转换后,在 Valgrind 将 IR 转换回机器代码并让主机处理器运行之前,工具(见下文)可以自由地对 IR 进行任何转换。即使它可以使用动态转换(即主机和目标处理器来自不同的体系结构),它也不能。Valgrind 重新编译二进制代码以在相同架构的主机和目标(或模拟)CPU 上运行。

于 2009-04-19T21:17:06.943 回答
1

如果您可以发布更多代码,尤其是从 valgrind 认为错误所在的部分,这将非常有帮助。

如果每次实例化类时都会发生这种情况,那么您可能忘记初始化构造函数中的成员之一。

是的:你应该担心这个错误,那些家伙真的会咬你。

于 2009-04-19T19:34:42.553 回答
1

在 64 位机器上。通常, int 在内存中占用 4 个字节。但 long 将占用 8 个字节的内存。因此,只需引用一个 int 值,因为长格式会导致完全不正确的结果。在这种情况下需要转换。

于 2012-04-30T18:04:53.537 回答
-1

该错误似乎不是来自您的代码,而是来自您正在使用的库。

Valgrind 带有一些默认的错误抑制,但这可能不包括您正在使用的库。

错误检查工具可检测基本库中的大量问题,例如 GNU C 库和 X11 客户端库,这些库预装在您的 GNU/Linux 系统上。你不能轻易地修复这些,但你不想看到这些错误(是的,有很多!)所以 Valgrind 读取错误列表以在启动时抑制。构建系统时,./configure 脚本会创建一个默认抑制文件。

您可以创建自己的错误抑制,您知道这些错误抑制与您的代码无关。

查看类似的 SO 问题为什么 Valgrind 不喜欢我使用 glutCreateWindow?

于 2009-04-20T03:03:40.790 回答