在数据流处理程序中获取segfault



我正在编写一个程序,该程序处理对新节点和边的for中的图的批量更新。我最近加入了一个滑动窗口方案,它检查图形中已经存在的边是否在窗口中,如果没有,则删除它们。我使用的边缘和节点类如下:

class Edge
{
public:
 uint64_t source;
 uint64_t target;
 unsigned type;
 std::string label;
 uint64_t timestamp;
 bool directed;
 bool extracted;
 Edge(){}
 Edge(Edge *e);
 Edge(uint64_t, uint64_t, unsigned, std::string, time_t, bool);
 bool operator ==(const Edge *other)
  {
  return((this->source==other->source)&&(this->target==other->target)&& 
          (this->type==other->type));
  }
};   
class Node
{
  public:
  uint64_t id;
  unsigned type;
  std::string label;
  uint64_t timestamp;
  std::vector<Edge *> adjacent_edges;
  Node(){}
  Node(Node *);
  bool push_edge(Edge *e)
  {
    try
    {
     adjacent_edges.push_back(e);
    }
    catch(std::bad_alloc&)
    {
     std::cout<<"Error pushing edge"<<std::endl;
     return false;
    }
    return true;    
   }
   std::vector<Edge *>::iterator pop_edge(std::vector<Edge *>::iterator e_it)
   {
    return adjacent_edges.erase(e_it);
   }
  bool operator ==(const Node *other)
   {
   return (this->id == other->id);
   }
};

在使用一个数据集时,我在尝试使用边缘迭代器访问边缘时,处理了69个滑动窗口大小为5的批处理文件,得到了一个segfault。在使用另一个数据集时,我在尝试删除邻接列表中的一个非空Edge指针(试图释放内存)时,在69个批处理文件后得到了一个segfault。我竭尽全力想弄清楚出了什么问题。这个程序的非滑动窗口版本运行得很好。此外,我知道使用STL deque数据结构会更好地用于滑动窗口。然而,我正在处理相当大的代码,我希望能够在不使用deque的情况下解决这个问题。提前谢谢。编辑:它发生在两条不同的线路上:

  for (int i = 0; i < node_list.size(); i++)
  {
  vector<Edge *>::iterator adj_it;
  for (adj_it = (node_list[i])->adjacent_edges.begin(); adj_it != (node_list[i])->adjacent_edges.end(); ++adj_it )
  {

      if ((max_batch_num - (*adj_it)->timestamp) > time_window)
      {

          deleteEdge(adj_it);
          num_edges_deleted++;
          --adj_it;
      }
  }
}

它发生在线路上:

if ((max_batch_num - (*adj_it)->timestamp) > time_window)

关于使用第一个数据集。这里的问题是,即使向量不是空的,向量中的指针也指向不属于应用程序的内存。当我使用gdb尝试打印时:

print (*adj_it)->timestamp

它给出:尝试获取不在内存中的值的地址

但这不应该发生,因为边正在被添加到相邻列表中。在使用第二个数据集时,当我使用时会发生错误

delete (*adj_it); 

其中adj_it是用于邻接列表向量的迭代器。

同样奇怪的是,如果我将滑动窗口增加"n",那么在"n"个批次之后也会出现同样的问题。

添加deleteEdge功能:

vector<FSM::Edge *>::iterator FSM::Graph::deleteEdge(vector<Edge *>::iterator e_it)
{
 //cout<<"Deleting edge: e "<<e->source<<" -> "<<e->target<<endl;//DEBUG
 FSM::Node *s = getNode((*e_it)->source);
 FSM::Edge *e_tmp = (*e_it);
 e_it = s->pop_edge(e_it);
 if (e_tmp != NULL)
 {
     delete e_tmp;
 }
 else
 {
     std::cerr<<"Trying to delete an Edge pointer which is NULL"<<endl;
     exit(1);
 }
 return e_it;

}

此外,我以前只使用索引,在@Julius回答后我又尝试了一次。这是我的新删除循环。

for (int j = 0; j<(node_list[i])->adjacent_edges.size();j++)
   {
       if ((max_batch_num - ((node_list[i])->adjacent_edges[j])->timestamp) > time_window)
               {                     
                   (node_list[i])->adjacent_edges.erase((node_list[i])->adjacent_edges.begin() + j);
                  --j;
                  num_edges_deleted++;
              }
  }

然而,不管怎样,我都会犯同样的错误。

顺便说一句。我真的很感谢到目前为止所有的评论。谢谢你抽出时间。

编辑:使用valgrind在代码的不同部分发现内存泄漏。去掉那个代码(对算法来说并不是必要的)就去掉了它。我接受@Julius的回答,因为根据我最初的说法,它会解决问题。还要感谢@RetiredNinja、@Beta和@Golazo的精彩评论。

for (int i = 0; i < node_list.size(); i++)
  {
  vector<Edge *>::iterator adj_it;
  for (adj_it = (node_list[i])->adjacent_edges.begin(); adj_it != (node_list[i])->adjacent_edges.end(); ++adj_it )
  {

      if ((max_batch_num - (*adj_it)->timestamp) > time_window)
      {

          deleteEdge(adj_it);
          num_edges_deleted++;
          --adj_it;
      }
  }
}

您正在删除边,然后使用--adj_it向后移动,然后使用deleteEdge迭代回刚才删除的边,因为for循环执行++adj_it。然后尝试检查已删除(无效)Edge对象的时间戳对象,从而导致segfault。

要么就是从Edge*向量中删除对象,然后使迭代器无效。

重要的是迭代器不是索引。你不能只擦除一个元素,然后就做--adj_it。在这种情况下,使用索引会更容易,因为你可以删除边缘对象,从向量中删除边缘指针,然后像你做的那样在做--adj_it之后继续循环。迭代程序只是比向量上的索引慢。

最新更新