【问题标题】:Valgrind: Invalid read of size 8 errorValgrind:无效读取大小 8 错误
【发布时间】:2016-06-05 02:30:42
【问题描述】:

当我通过 valgrind 运行我的程序时,我得到以下信息

==29852== Invalid read of size 8
==29852==    at 0x4EDEA50: std::_Rb_tree_increment(std::_Rb_tree_node_base const*) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==29852==    by 0x414EEA: std::_Rb_tree_const_iterator<std::pair... >::operator++() (stl_tree.h:284)
==29852==    by 0x4268CF: Tree::removeConstantsPair(std::set...) (Tree.h:65)
==29852==    by 0x4239C4: yy_reduce(yyParser*, int) (parser.y:251)
==29852==    by 0x425F6D: Parse(void*, int, Token*, Tree*) (parser.c:1418)
==29852==    by 0x404837: main (main.cpp:95)

Tree.h 中的第 65 行是

inline void removeConstantsPair(set<pair<string, string>>& vec){
    set<string>::iterator itr; 
    for(auto &v : vec){ //This is line 65
        itr = domainList.find(v.first);
        if(itr != domainList.end())
            vec.erase(v);
    }
}

但是泄漏摘要说没有丢失的内存。 据我了解,如果我从已释放的内存中读取,则会发生无效读取,因此在我的情况下,&amp;vec 之前必须已被释放。我的程序虽然运行但不会崩溃。

谁能解释为什么会出现内存读取错误。

【问题讨论】:

  • std::set::erase() 使被擦除的迭代器无效。如果将增量应用于已擦除的迭代器怎么办?这就是错误报告的样子(使用了operator++())。

标签: c++ c++11 memory-management valgrind


【解决方案1】:

问题很可能是由以下线路引起的:

        vec.erase(v);

使用范围 for 循环并从容器中删除项目不是一个好主意。将循环更改为:

for ( auto iter = vec.begin(); iter != vec.end(); /* Empty on purpose*/ )
{
   if(domainList.find(iter->first) != domainList.end())
   {
      iter = vec.erase(iter);
   }
   else
   {
      ++iter;
   }
}

【讨论】:

  • 成功了!是否有任何可能捕获此类行为的 g++ 编译器标志?
  • @SamidhT,我不知道。
【解决方案2】:

以下小程序在Visual Studio编译器下运行时出现错误:

#include <string>
#include <set>
#include <map>

std::set<std::string> domainList = {"abc", "123", "456"};

using namespace std;

void removeConstantsPair(set<pair<string, string>>& vec)
{
    set<string>::iterator itr; 
    for(auto &v : vec)
    { 
        itr = domainList.find(v.first);
        if(itr != domainList.end())
            vec.erase(v);  // <--erasing this iterator makes it invalid
    }
}

int main()
{
    std::set<std::pair<string, string>> vec = {make_pair("abc", "xyz"), 
                                               make_pair("456", "xyz"),
                                               make_pair("000", "xyz")};
    removeConstantsPair(vec);
}

当在 for 循环中尝试对已擦除的迭代器进行递增时,Visual Studio 调试运行时会断言“表达式映射/设置迭代器不可递增”。

所以解决方案是确保要递增的迭代器不是被擦除的迭代器。

void removeConstantsPair(set<pair<string, string>>& vec)
{
    set<string>::iterator itr;
    auto iterSet = vec.begin();
    while (iterSet != vec.end())
    {
        itr = domainList.find((*iterSet).first);
        if (itr != domainList.end())
        {
            auto erasedIter = iterSet;
            ++iterSet;
            vec.erase(erasedIter);
        }
        else
            ++iterSet;
    }
}

【讨论】:

    【解决方案3】:

    “无效的内存读取”也可能由于许多其他原因而发生,除了问题中所述的原因。

    一个例子:

    通过new 分配内存的请求通常比新类的实例所需的内存多一点。例如,特定的 C++ 实现可能会以 16 字节的倍数分配内存。因此,new 的 sizeof 将返回 12 的类的实例实际上最终会分配 16 个字节,并且尝试读取实际实例化对象的末尾将最终被 valgrind 正确标记为无效的内存读取。

    【讨论】:

      猜你喜欢
      • 2015-07-06
      • 1970-01-01
      • 1970-01-01
      • 2014-05-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-09
      • 1970-01-01
      相关资源
      最近更新 更多