【问题标题】:Iterator invalid read of size 4大小为 4 的迭代器无效读取
【发布时间】:2016-11-17 18:58:48
【问题描述】:

为什么 Valgrind 会在下面的行中指明 Invalid read of size 4

for (map<uint16_t, SPacket *>::iterator it = m_PacketMap.begin() ; it != m_PacketMap.end(); ++it) 
{
    if (it->first < ackNumber)
    {
        if (it->second->data) delete [] it->second->data;
        if (it->second) delete it->second;
        m_PacketMap.erase(it);
    }
}

我在循环之前验证了m_PacketMap.size() &gt; 0,并在循环之前临时添加了调试以验证 m_PacketMap 内容,但一切看起来都符合预期。这是 Valgrind 错误消息, RadioManager.cpp:1042 是上面的行:

==5535== Invalid read of size 4
==5535==    at 0x421EBE5: std::_Rb_tree_increment(std::_Rb_tree_node_base*) (in /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16)
==5535==    by 0x80AD20D: RadioManager::DecodeAcknowledgementNumber(unsigned char*, unsigned int) (RadioManager.cpp:1042)

SPacket 和 m_PacketMap 是这样定义的

typedef struct SPacket
{
    uint8_t * data;
    size_t size;
    timeval tval;
} SPacket;
map<uint16_t, SPacket *> m_PacketMap;

我的迭代器是否存在问题、_Rb_tree_increment 中可能存在的问题,或者完全是其他问题?

【问题讨论】:

  • 是的。如果它们与键匹配,我将删除地图条目。我会将正文添加到原始问题中。啊。我想我明白你在说什么。容器收缩时迭代器如何工作。

标签: c++ dictionary iterator valgrind


【解决方案1】:

必须小心地从for 循环中删除容器元素...

你必须这样做:

map<uint16_t, SPacket *>::iterator it = m_PacketMap.begin()
while ( it != m_PacketMap.end() ) 
{
    if (it->first < ackNumber)
    {
        if (it->second->data) delete [] it->second->data;
        if (it->second) delete it->second;
        // it updated by erase, no need to increment
        it = m_PacketMap.erase(it);
    }
    else
    {
        // move to next item
        ++it;
    }
}

这就是必须编写删除容器某些元素的循环的方式(对于集合、向量...的工作方式相同)。

对于地图,以上代码仅适用于 C++11。如果您使用较早的版本,根据this post(未测试),您应该这样做:

for ( map<uint16_t, SPacket *>::iterator it = m_PacketMap.begin(); it != m_PacketMap.end();  ) 
{
    if (it->first < ackNumber)
    {
        if (it->second->data) delete [] it->second->data;
        if (it->second) delete it->second;
        // it updated by erase, no need to increment
        m_PacketMap.erase(it++);
    }
    else
    {
        // move to next item
        ++it;
    }
}

因为擦除项目然后增加它(你最终对你的 for 循环所做的事情)会使你松散元素并可能通过 m_PacketMap.end() (然后让你的循环覆盖项目超出容器的限制)如果你从容器中删除最后一个元素。

【讨论】:

  • m_SentPacketMap 是一个错字
  • m_PacketMap.erase(it++) 应该是m_PacketMap.erase(it)吗?
  • it = m_PacketMap.erase(it); 用于 C++11,m_PacketMap.erase(it++); 用于早期版本(it = m_PacketMap.erase(it); 不应编译)
【解决方案2】:

你删除了一个元素,这使得it 无效,然后你增加了it。你不能增加一个无效的迭代器。

使用-D_GLIBCXX_DEBUG 重新构建您的代码将启用 libstdc++ 调试模式,该模式将在无效的迭代器操作时中止。

与 valgrind 错误无关,您不需要在使用 delete 之前测试指针不为空,因为在空指针上使用 delete 是安全的(它无论如何都会检查空,所以你只是添加了不必要的重复检查)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-02
    • 1970-01-01
    • 2019-02-04
    • 1970-01-01
    • 1970-01-01
    • 2015-05-09
    相关资源
    最近更新 更多