【问题标题】:Geting a segfault on in a data stream processing program在数据流处理程序中获取段错误
【发布时间】:2015-06-02 02:26:18
【问题描述】:

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

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 的批处理文件后,在尝试使用边迭代器访问边时出现段错误。在使用另一个数据集时,我在尝试删除邻接列表中的非空 Edge 指针(尝试释放内存)时,在 69 个批处理文件后出现段错误。我束手无策,试图弄清楚出了什么问题。该程序的非滑动窗口版本运行良好。我也知道使用 STL 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 是 adjacency_list 向量的迭代器。

同样奇怪的是,如果我通过说'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++;

              }

  }

但是,无论如何,我都会遇到同样的错误。

顺便说一句。到目前为止,我真的很感谢所有的 cmets。感谢您的宝贵时间。

编辑:使用 valgrind 在代码的不同部分发现内存泄漏。摆脱那个代码(它对算法来说并不是真正必要的)摆脱了它。我接受@Julius 的回答,因为根据我原来的陈述它可以解决问题。还要感谢 @RetiredNinja、@Beta 和 @Golazo 的出色 cmets。

【问题讨论】:

  • 发生在哪一行?
  • 如果你有内存问题,首先要做的是摆脱原始的拥有指针
  • 是时候运行你的调试器了。
  • Golazo 我添加了发生段错误的行。我了解使用智能指针。但是,除非绝对必要,否则我不想遍历整个项目并摆脱所有智能指针。这将需要比我更多的时间。
  • 我在您的代码中看不到 deleteEdge 函数,但如果您在迭代时从容器中删除项目,那么您的循环不正确。一旦你删除了一个项目,迭代器就无效了,所以你不能用它来获取前一个项目。 erase 返回一个迭代器,你应该使用它,并且只在你不擦除项目时增加迭代器。这可能会有所帮助:stackoverflow.com/a/4645727/920069

标签: c++ segmentation-fault sliding-window data-stream


【解决方案1】:
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 对象的时间戳对象,从而导致段错误。

要么这样,要么你正在从 Edge* 向量中删除对象,然后使你的迭代器无效。

重要的是迭代器不是索引。您不能只擦除一个元素然后执行 --adj_it。这是使用索引更容易的情况,因为您可以删除边缘对象,从向量中删除边缘指针,然后在执行 --adj_it 之后继续循环。顺便说一句,迭代器只是比向量上的索引慢。

【讨论】:

  • 我在倒退,因为我使用了向量擦除方法,它从 adjacency_list 中删除边缘,然后在它前进之后将所有其他元素带入。 @RetiredNinja 的评论说这是错误的,因为擦除返回一个新的迭代器,我不能使用以前的迭代器转到上一个项目。希望我能正确理解您的回答。
  • 我之前用的是索引,我又试了一次。同样的错误。我无法理解的是,如果相邻边的大小不为 0,那里怎么会有指向非法内存的指针。特别是因为该边缘是在上一批中添加的。
  • 为什么向量中可能存在无效项的简单答案是您的代码以某种方式将它们放在那里。原始指针是危险的,并且不能保证您不会重复删除某些内容或只是将一些不好的东西推入其中。
  • @RetiredNinja:我明白。不幸的是,如果我必须摆脱原始指针,我必须检查整个项目并将大量位置的指针更改为智能指针或只是对象。我的下一个项目没有更多的原始指针。但是对于这个,有没有什么快速的方法可以在指针向量上实现滑动窗口?
猜你喜欢
  • 1970-01-01
  • 2021-09-18
  • 2018-11-18
  • 1970-01-01
  • 1970-01-01
  • 2023-03-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多