Valgrind 抱怨使用依赖于未初始化字节的条件跳转

Valgrind complains about using conditional jumps that depend on uninitialized bytes

这个问题出现在我正在处理的一个较大程序的模块中,这里是一个小模拟:

#include <iostream>
#include <vector>

using namespace std;

struct node{
    int key;
    vector<int> connections;
};

int main(void){

    int size = 10;
    node *A;

    A=(node*)malloc((size)*sizeof(node));

    int i;
    for(i=0;i<size;i++){
        A[i].key = i;
        if(i%2 == 0) A[i].connections.push_back(i);
    }

    for(i=0;i<size;i++) {
        if(A[i].connections.size() > 0) {
            cout << A[i].key << "---" << A[i].connections.size() << endl;
        }
    }

    free(A);

    return 0;
}

这是我在运行valgrind ./testCode

时得到的一小部分
==30433== Conditional jump or move depends on uninitialised value(s)
==30433==    at 0x400F63: std::vector<int, std::allocator<int> >::push_back(int const&) (stl_vector.h:915)
==30433==    by 0x400DF0: main (testCode.cpp:25)

根据我的理解,问题在于向量的 push_back 函数中有一个 if 语句使用了一些未初始化的值。我不确定到底是什么。玩了一会发现问题出在我用的是malloc。如果我使用 calloc 我根本不会收到任何警告。如果我使用 new 也是一样。然而,在所有情况下,程序的行为方式完全相同。这里发生了什么?即使我收到所有这些警告,使用 malloc 是否安全?

未设置节点内的连接。这是因为您使用 malloc 来创建您的 Node 实例。那只会给你原始的未初始化内存。你必须使用 new 在 c++ 程序中分配对象而不是 malloc。 New 分配内存然后运行构造函数

node *A = new Node();

并删除以释放它 - 不是免费的

事实上,您需要一个 Node 对象数组。所以按照本所说的去做。制作它们的矢量

但是无论如何,永远不要在 C++ 程序中使用 malloc / free 除非你真的确定你知道你在做什么

很有可能是因为malloc没有调用node的构造函数。由于这是 c++,node 不是 c 中的结构,而是 class。它有一个隐式构造函数,需要在实例化时调用。这意味着向量也没有正确创建。

你应该做两件事:

  1. 避免调用 new,在这种情况下没有必要,只需使用 node A[10] 或更好的 std::vector<node> A
  2. 如果您不能这样做,请始终在 C++ 中使用 new,以便正确创建对象。

如果你绝对必须使用 malloc,那么无论如何你都需要调用 placement new 来构建你的结构 (new (&A[0]) node()),所以真的不要使用 malloc.

不,这里使用malloc不安全。

malloc是C分配内存的方式。它不考虑 C++ 构造函数。

calloc 也是 C,但它会零初始化您的内存。这就是 std::vector 起作用的原因(然而,它也是未定义的行为,因为可能有一个 vector 实现需要初始化为非零的其他值)。

有很多方法可以解决这个问题:

  • 改用new Node

  • 如果你做不到,你至少可以在 malloc returns (new (ptr) Node()).[=22= 的内存上做一个新的放置]

  • 使用智能指针和make_sharedmake_unique。这样一来,您甚至不需要考虑释放内存。

  • 最好的方法可能是使用 std::vector(如另一个答案中所建议的)或 std::array.

由于您使用的是 vector<>,因此您应该始终避免使用 malloc 和 free,因为它们不会初始化构造函数,而是使用 new 和 delete 以及智能指针。

但是,很少需要使用 new/delete,因为 vector 使用类似的内存和访问足迹完成同样的事情。

此实现将 node 包装在向量中并消除了直接内存分配,因为它由 vector<> 自动完成,同时保持相同的传统使用方式。

#include <iostream>
#include <vector>
using namespace std;

struct node {
    int key;
    vector<int> connections;
};

int main(void) {
    // original with similar memory footprint and access characteristics using modern C++ vector<>
    int size = 10;
    vector<node> A(size);

    int i;
    for (i = 0; i<size; i++) {
        A[i].key = i;
        if (i % 2 == 0) A[i].connections.push_back(i);
    }

    for (i = 0; i<size; i++) {
        if (A[i].connections.size() > 0) {
            cout << A[i].key << "---" << A[i].connections.size() << endl;
        }
    }
    return 0;
}