删除以struct为键的地图时的分段错误

时间:2012-02-29 15:51:10

标签: c++ map struct segmentation-fault destructor

在我的代码中,我有一个用作地图键的结构:

struct index{
  int x;
  int y;
  int z;
  bool operator<(const index &b){
    bool out = true;
    if ( x == b.x ){
      if ( y == b.y ){
        out = z < b.z;
      } else out = y < b.y;
    } else out = x < b.x;
    return out;
  }
};

我使用的地图是:

map<index,set<std::pair<int, int> > > tBoxes;

当程序结束运行时,我试图释放内存,程序试图删除地图时出现分段错误。使用gdb我得到:

#0  std::_Rb_tree<membrane::index, std::pair<membrane::index const, std::set<std::pair<int, int>, std::less<std::pair<int, int> >, std::allocator<std::pair<int, int> > > >, std::_Select1st<std::pair<membrane::index const, std::set<std::pair<int, int>, std::less<std::pair<int, int> >, std::allocator<std::pair<int, int> > > > >, std::less<membrane::index>, std::allocator<std::pair<membrane::index const, std::set<std::pair<int, int>, std::less<std::pair<int, int> >, std::allocator<std::pair<int, int> > > > > >::_M_erase (this=0x63b900, 
    __x=0x404057eb274ede6c)
    at /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_tree.h:1264
#1  0x000000000041ed2f in std::_Rb_tree<membrane::index, std::pair<membrane::index const, std::set<std::pair<int, int>, std::less<std::pair<int, int> >, std::allocator<std::pair<int, int> > > >, std::_Select1st<std::pair<membrane::index const, std::set<std::pair<int, int>, std::less<std::pair<int, int> >, std::allocator<std::pair<int, int> > > > >, std::less<membrane::index>, std::allocator<std::pair<membrane::index const, std::set<std::pair<int, int>, std::less<std::pair<int, int> >, std::allocator<std::pair<int, int> > > > > >::_M_erase (
    this=0x63b900, __x=0x7d31110)
    at /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_tree.h:1264
#2  0x000000000041ed2f in std::_Rb_tree<membrane::index, std::pair<membrane::index const, std::set<std::pair<int, int>, std::less<std::pair<int, int> >, std::allocator<std::pair<int, int> > > >, std::_Select1st<std::pair<membrane::index const, std::set<std::pair<int, int>, std::less<std::pair<int, int> >, std::allocator<std::pair<int, int> > > > >, std::less<membrane::index>, std::allocator<std::pair<membrane::index const, std::set<std::pair<int, int>, std::less<std::pair<int, int> >, std::allocator<std::pair<int, int> > > > > >::_M_erase (
    this=0x63b900, __x=0x7d30e30)
    at /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_tree.h:1264
#3  0x000000000041ed2f in std::_Rb_tree<membrane::index, std::pair<membrane::index const, std::set<std::pair<int, int>, std::less<std::pair<int, int> >, std::allocator<std::pair<int, int> > > >, std::_Select1st<std::pair<membrane::index const, std::set<std::pair<int, int>, std::less<std::pair<int, int> >, std::allocator<std::pair<int, int> > > > >, std::less<membrane::index>, std::allocator<std::pair<membrane::index const, std::set<std::pair<int, int>, std::less<std::pair<int, int> >, std::allocator<std::pair<int, int> > > > > >::_M_erase (
    this=0x63b900, __x=0x7d30b50)
    at /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_tree.h:1264
#4  0x000000000041ed2f in std::_Rb_tree<membrane::index, std::pair<membrane::index const, std::set<std::pair<int, int>, std::less<std::pair<int, int> >, std::allocator<std::pair<int, int> > > >, std::_Select1st<std::pair<membrane::index const, std::set<std::pair<int, int>, std::less<std::pair<int, int> >, std::allocator<std::pair<int, int> > > > >, std::less<membrane::index>, std::allocator<std::pair<membrane::index const, std::set<std::pair<int, int>, std::less<std::pair<int, int> >, std::allocator<std::pair<int, int> > > > > >::_M_erase (
    this=0x63b900, __x=0x7d272c0)
    at /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_tree.h:1264
#5  0x0000000000418237 in clear (this=0x63b3e0, 
    __in_chrg=<value optimized out>)
    at /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_tree.h:692
#6  clear (this=0x63b3e0, __in_chrg=<value optimized out>)
    at /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/stl_map.h:501
#7  membrane::~membrane (this=0x63b3e0, __in_chrg=<value optimized out>)
    at memMC.cpp:135
#8  0x0000000000421658 in __tcf_5 () at main.cpp:7
#9  0x000000378ea334f5 in exit () from /lib64/libc.so.6
#10 0x000000378ea1d99b in __libc_start_main () from /lib64/libc.so.6
#11 0x0000000000402309 in _start ()

使用valgrind(运行$valgrind -v --track-origins=yes --leak-check=yes --tool=massig --tool=memcheck --read-var-info=yes --log-file=error.txt ./mem)我得到:

==2561==     in use at exit: 0 bytes in 0 blocks
==2561==   total heap usage: 3,346,323 allocs, 3,346,323 frees, 98,851,279 bytes allocated
==2561== 
==2561== All heap blocks were freed -- no leaks are possible
==2561== 
==2561== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4)
--2561-- 
--2561-- used_suppression:      4 dl-hack3
==2561== 
==2561== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4)

这很烦人,因为它没有告诉我什么。

我试图手动清除地图(同时打印一些可能提供一些线索的输出:

while (tBoxes.size() > 0){
   printf ("%i  ", (*tBoxes.begin()).first.x);
   printf ("%i  ", (*tBoxes.begin()).first.y);
   printf ("%i\n", (*tBoxes.begin()).first.z);
   while ((*tBoxes.begin()).second.size() > 0){
     printf ("a\n");      
     tgBit1 = (*tBoxes.begin()).second.begin();
     (*tBoxes.begin()).second.erase(tgBit1);
     printf ("b\n");
   }
   tBoxes.erase(tBoxes.begin());
 }
 tBoxes.clear();
 tNextBoxes.clear();

我可以收集到的是当我从0增加x到1时删除崩溃并且在后发生我删除了集合的内容。

为什么会发生这种情况,除了使用clear()之外还有办法清除地图吗?

修改

我找到了这个bug。我使用的向量之一没有正确设置,实际上是大小为0.但是,我仍然访问该向量中的元素。

向量是double值,我在除以框大小后使用此值并向下舍入结果以确定要放入一对的tBox。我假设我有一个无效要在index中使用的值。问题仍然存在,为什么当我试图访问这个元素时程序没有崩溃......?

1 个答案:

答案 0 :(得分:2)

我的猜测是,程序中的其他地方会发生某种堆/堆栈损坏,只有在您的映射尝试访问错误的指针/迭代器时才会显示。查看最后一个被擦除的迭代器(0x404057eb274ede6c),它与之前的迭代器值(0x7d31110)相比看起来很可疑。

至于为什么Valgrind没有发现这种腐败?我对这个工具并不是很熟悉,但SE上的this question指出了另一个案例,即Valgrind未能发现“明显的”不良行为,并建议其他一些东西来帮助检测它。

我会尝试将崩溃减少到尽可能少的代码。在另一个项目中创建一个小测试用例,看看是否仍然发生崩溃。在调试模式下运行和/或使用额外的内存检查来查看是否可以捕获内存损坏(假设它是什么)。

更新编辑:

假设您正在使用std::vector::operator[],则此方法不会执行任何边界检查,并且很乐意让您尝试访问不存在的元素。我认为这被指定为未定义的行为,不幸的是,一个可能的结果是没有任何反应。

如果您希望或需要检查边界,请使用std::vector::at(),有关详细信息,请参阅this question。通常,只有在知道所使用的索引在边界内时才使用[],例如在迭代循环中或手动检查索引时。