大小为 4 的迭代器无效读取
Iterator invalid read of size 4
为什么 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() > 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 中可能存在问题,或者完全是其他问题?
从 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()
(然后让你的循环覆盖项目超出容器的限制)如果您从容器中删除最后一个元素。
你擦除一个元素,这使得 it
无效,然后你增加 it
。您不能增加无效的迭代器。
使用 -D_GLIBCXX_DEBUG
重建您的代码将启用 libstdc++ 调试模式,这将在无效的迭代器操作时中止。
与 valgrind 错误无关,在使用 delete
之前不需要测试指针是否为空,因为在空指针上使用 delete
是安全的(它检查空指针无论如何,所以你只是添加了不必要的重复检查。
为什么 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() > 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 中可能存在问题,或者完全是其他问题?
从 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()
(然后让你的循环覆盖项目超出容器的限制)如果您从容器中删除最后一个元素。
你擦除一个元素,这使得 it
无效,然后你增加 it
。您不能增加无效的迭代器。
使用 -D_GLIBCXX_DEBUG
重建您的代码将启用 libstdc++ 调试模式,这将在无效的迭代器操作时中止。
与 valgrind 错误无关,在使用 delete
之前不需要测试指针是否为空,因为在空指针上使用 delete
是安全的(它检查空指针无论如何,所以你只是添加了不必要的重复检查。