为什么 Valgrind 在我的 C++ 矢量访问中报告“未初始化值”而不是“无效读取”?

问题描述 投票:0回答:1

我正在开发一个 C++ 程序,该程序涉及访问 std::vector 中的元素。我在使用 Valgrind 运行代码时遇到了一个问题,它报告“未初始化值”错误,但不是我预期的“无效读取”错误。这是我的代码的简化版本:

#include <vector>
#include <iostream>
#define DATA_SIZE 10

struct Data {
  Data(double aa, double bb) : a(aa), b(bb) {}
  double a {0.0};
  double b {0.0};
};

class Test {
  public:
    Test() {
      data_.clear();
      data_.reserve(DATA_SIZE * 2);
      for (int i = 0; i < DATA_SIZE; ++i) {
          data_.push_back(Data(i, i));
      }
    }
    double Read() {
      const int index = DATA_SIZE;
      const auto& data = data_[index];
      double res = data.a + data.b;
      std::cout << res;
      return res;
    }
  private:
    std::vector<Data> data_;
};

int main() {
  Test t;
  t.Read();
}

Machine
:Linux ubuntu 18.04,带有
g++ 7.5
valgrind 1:3.13.0-2ubuntu2.3

Compile

g++ -std=c++17 -O2 -g test.cpp -o test

Run

valgrind --tool=memcheck --leak-check=full --expensive-definedness-checks=yes --track-origins=yes ./test

结果

Valgrind 在

Conditional jump or move depends on uninitialised value(s)
上报告了很多
operator<<(std::cout << res;)

问题:

我期望 Valgrind 报告“无效读取”,因为访问超出范围,但它只报告“未初始化值”错误。为什么 Valgrind 报告“未初始化值”而不是“无效读取”?

更多信息:

有趣的是,如果我改变

data_.reserve(DATA_SIZE * 2);
data_.reserve(DATA_SIZE);
,Valgrind 报告“无效读取”(如预期)。我觉得问题在于矢量储备的大小,但我不太清楚为什么/那是什么。

c++ memory-leaks valgrind
1个回答
0
投票

Memcheck 的工作级别比高级源代码低得多。它对 std::vector 一无所知。它看到的只是分配(使用operator new)以及读取和写入(以及系统调用)。因此,当您编写

data_.reserve(DATA_SIZE * 2);
memcheck 时,会将底层调用重定向到operator new 并记录以默认对齐方式分配的 320 字节。在内部,它将分配标记为未初始化的影子内存。

当你循环浏览时

for (int i = 0; i < DATA_SIZE; ++i) {
          data_.push_back(Data(i, i));
      }

底部 160 字节被标记为已初始化。然后当你阅读时

      const int index = DATA_SIZE;
      const auto& data = data_[index];

它仍在 320 字节块的范围内,因此可以访问。但影子内存表明它尚未初始化。不过还没有错误,memcheck 允许复制未初始化的内存。

当您到达

cout
时,您就会收到错误消息。在从 double 到某种字符串的转换过程中,会出现几个条件(是否为负数?指数是否为负数?指数是否大于 1e6 等等)。对未初始化值的这些条件检查将触发错误。 (这些可能不是触发错误的确切位置,这只是一个示例)。

© www.soinside.com 2019 - 2024. All rights reserved.