为啥我在这个迭代器中遇到分段错误?

Posted

技术标签:

【中文标题】为啥我在这个迭代器中遇到分段错误?【英文标题】:Why am I getting a segmentation fault in this iterator?为什么我在这个迭代器中遇到分段错误? 【发布时间】:2015-02-10 19:58:20 【问题描述】:

我正在编写一个程序,该程序应该查看图表并计算需要删除的最小边数,以离开每个连接组具有偶数个顶点的森林。我知道如何解决这个问题,但是当我尝试遍历一个列表时,我遇到了分段错误并且不知道为什么。

#include <cmath>
#include <cstdio>
#include <list>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

int main() 
    /* Enter your code here. Read input from STDIN. Print output to STDOUT */

    int N;
    cin >> N;
    int M; 
    cin >> M;

    // matrix of adjacency list to hold node values
    vector<list<int> > adjList(N, list<int>());

    // find the number of children nodes each node has
    int ui, vi;
    for (int i = 0; i < M; i++) 
      cin >> ui;
      cin >> vi;
      ui--;
      vi--;
      //cout << ui << " " << vi << endl;
      adjList[ui].push_back(vi);
      adjList[vi].push_back(ui);
      //cout << "list length: " << adjList[ui].size() << endl;
    
    //cout << "after for loop" << endl;
    // count the number of nodes with even numbers of children

    for (int i = 0; i <= M; i++) 
      cout << i << "-> ";
      for (list<int>::iterator it = adjList[i].begin(); it != adjList[i].end(); it++) 
        cout << *it << " ";
      
      cout << endl;
    

    int edgesRemoved = 0;

    for (int i = 0; i <= M; i++) 
      for (list<int>::iterator it = adjList[i].begin(); it != adjList[i].end(); ++it) 
        int j = *it;
        if (adjList[j].size() % 2 == 1) 
          // delete vertex from current list
          cout << "test" << endl;
          adjList[i].erase(it);

          // delete vertex on the other list
          cout << "test" << endl;
          cout << j << endl;
          cout << *adjList[j].begin() << endl;
          for (list<int>::iterator it2 = adjList[j].begin(); it2 != adjList[j].end(); ++it2) 
            cout << *it2 << " ";
            if (i == *it2) 
              adjList[j].erase(it2);
            
          

          edgesRemoved++;
        
      
    
    cout << edgesRemoved << endl;
    return 0;

使用 cout 语句调试程序后,我发现问题出在这里:

for (int i = 0; i <= M; i++) 
      for (list<int>::iterator it = adjList[i].begin(); it != adjList[i].end(); ++it) 
        int j = *it;
        if (adjList[j].size() % 2 == 1) 
          // delete vertex from current list
          cout << "test" << endl;
          adjList[i].erase(it);

          // RIGHT UNDER HERE
          //    vvvvvvvvvv

          for (list<int>::iterator it2 = adjList[j].begin(); it2 != adjList[j].end(); ++it2) 
            cout << *it2 << " ";
            if (i == *it2) 
              adjList[j].erase(it2);
            
          

          edgesRemoved++;
        
      
    

在程序创建一个迭代器后,我得到一个分段错误,该迭代器旨在遍历向量中的另一个列表。我不明白为什么,语法与第一个 for 循环相同,另一个迭代器遍历第一个列表。

这是一个示例,说明在我输入代表树的数字的输入后,程序会打印邻接矩阵并继续进行实际计算(这部分工作正常,这是计算过程中的最终结果):

10 9
2 1
3 1
4 3
5 2
6 1
7 2
8 6
9 8
10 8
0-> 1 2 5 
1-> 0 4 6 
2-> 0 3 
3-> 2 
4-> 1 
5-> 0 7 
6-> 1 
7-> 5 8 9 
8-> 7 
9-> 7 
test
test
1
0
Segmentation fault

【问题讨论】:

请发Minimal Complete Verifiable Example 现在是时候使用真正的调试器了,而不是cout 【参考方案1】:

擦除迭代器下的项会使迭代器失效;进一步使用它会导致未定义的行为,因此任何事情都可能发生。这类事情的惯用语是:

std::list<int>::iterator it = adjList[i].begin();
while ( it != adjList[i].end() ) 
    if ( *it == i ) 
        it = adjList[j].erase( it );
     else 
        ++ it;
    

erase 函数将一个迭代器返回到紧跟被删除元素之后的元素。

这对所有序列类型都有效,而不仅仅是std::list

【讨论】:

【参考方案2】:

在迭代列表时,不能删除列表中的当前项(迭代器指向的东西)。你可以这样做:

adjList[j].erase(it2++);

但是,afaik,在迭代时既不缩小也不扩大列表被认为是最佳实践。

【讨论】:

那么在这种情况下,应该如何删除列表中不在开头或结尾的特定元素?还是在这种情况下使用邻接矩阵是一个更好的主意? 我在答案中包含的代码会做到这一点。您使用迭代器上的后修复增量删除。 使用std::list 的全部意义在于能够在任意点删除和插入,即在您对其进行迭代时。

以上是关于为啥我在这个迭代器中遇到分段错误?的主要内容,如果未能解决你的问题,请参考以下文章

向量和迭代器的分段错误? [关闭]

分段错误(核心转储) - 无法访问的计数器值

迭代器偶尔会出现分段错误

为啥迭代器指向的数据在从列表中删除后保留

golang中的那些坑之迭代器中的指针使用

反应警告:数组或迭代器中的每个孩子都应该有一个唯一的“key”道具。检查`App`的渲染方法