在某些循环内使用 vector.push_back 时出现分段错误

Posted

技术标签:

【中文标题】在某些循环内使用 vector.push_back 时出现分段错误【英文标题】:Segmentation fault when using vector.push_back inside of certain loops 【发布时间】:2020-02-25 11:35:47 【问题描述】:

根据教授的要求,我目前正在 Cygwin 终端上使用 g++。

我应该接收一个输入文件并逐字阅读,然后将所有单词放入一个向量中,按字母顺序排序且没有重复。

但是,每次我尝试在某些循环内操作我的向量(即 - push_back)时,我的程序都会出现分段错误。

这是我的代码的 sn-p:

void word_count(ifstream& input)
    string temp;
    vector<string> v;

    input >> temp; //set first variable
    v.push_back(temp);

    while (!input.eof())  //I'm aware of the limitations while using !eof, this is just the way I am required to loop over a file
        input >> temp;

        for (vector<string>::iterator i = v.begin(); i != v.end(); i++)  //check entire vector for word
            if (*i == temp)  //just break and skip the word if it already exists
                break;
            
            if (i == v.end() - 1)  //if the word doesn't exist yet
                for (vector<string>::iterator k = v.begin(); k != v.end(); k++)  //re-search the vector for the proper place
                    if (k == v.end() - 1)  //if at the end, just push_back the vector
                        v.push_back(temp); //Causes segmentation fault
                        break;
                    
                    if ((*k < temp) && (*(k + 1) > temp))  //find correct place and insert the word in the vector
                        v.insert(k, temp); //Also causes segmentation fault if execution even manages to get this far
                    
                
            
        
    

第 5 行的第一个 push_back 非常好,我可以多次复制和粘贴而不会出错。我也可以在输入>> temp(在while循环内部)之后立即push_back而不会出错。但是,如果我在“k”循环下尝试 push_back,则会出现分段错误。我完全被难住了。

我尝试在 *** 上查看其他与矢量相关的问题,但我真的不明白为什么我可以(或不能)在某些地方使用 push_back。

提前感谢您的帮助!

编辑 1:我应该提一下,我在 VS 2019 中对其进行了测试。弹出向量库文件,说明抛出了“读取访问违规”异常。没有分段错误(或者这可能是 VS 告诉我发生分段错误的方式?)

编辑 2:修改向量会使迭代器无效。不知道,谢谢大家的帮助!

编辑 3:我只能使用向量,不能使用集合或其他容器。如果我可以使用一套,我完全会。

【问题讨论】:

If the new size() is greater than capacity() then all iterators and references (including the past-the-end iterator) are invalidated. Otherwise only the past-the-end iterator is invalidated. 这是来自push_back() 方法的cppreference,可能这是你的问题 您是否允许/应该使用标准算法?如果是的话,这对我来说就像en.cppreference.com/w/cpp/algorithm/lower_bound 的工作 您应该永远在迭代向量时对其进行修改,正如@Raffallo 正确提到的那样(只是将其分解一下)。也许考虑使用std::set 来节省您的大量工作。它是有序的并且只包含唯一的元素。就好像它就是为这个任务而生的。 好吧,或者你一旦修改它就立即停止迭代 - break 一旦你插入。 我不知道 push_back 会使迭代器无效。谢谢你们!我将不得不仔细检查有关向量的文档。我没有使用 因为我需要使用 来“跳出框框思考”,正如我的教授所说的那样(尽管正如你所说, 非常适合这项任务)。 【参考方案1】:

当你修改向量迭代器变得无效。

有两个原因:

当你 push_back 和 std::vector::capacity 被破坏时,分配新的块数据并将数据移动/复制到新缓冲区 当您在中间旧迭代器中添加/删除项目时,可能会指向可能不再存在的不同项目。

有快速修复它的方法。当您进行修改时,您必须获取迭代器的更新值。 poush_back 没有这样的功能,但是std::vector::insert 将迭代器返回到新值,并且这个迭代器可以用来更新 for 循环迭代器。

我可以修复你的代码,但它太复杂(缩进很多),我希望避免这种情况。你应该先把这段代码分割成更小的函数。

而不是挽救你的代码,这是我的版本:

template<typename Iter>
size_t count_unique_items(Iter begin, Iter end)

    using value_type = typename std::iterator_traits<Iter >::value_type;
    std::unordered_set<value_type> unique_items;

    std::copy(begin, end, std::inserter(unique_items, unique_items.end()));

    return unique_itmes.size();


size_t count_unique_words(std::istream& input)

    return count_unique_items(std::istream_iterator<std::string>input, );

https://wandbox.org/permlink/bHji7JZoB7E9ZoLn

【讨论】:

很遗憾,我不能使用 ,只能使用 。不过谢谢你帮助我!我不知道 push_back 使迭代器失效。【参考方案2】:

在迭代时修改向量可能会使迭代器无效,然后任何事情都可能发生。

但是你把事情复杂化了——由于向量是有序的,你不需要先看字符串是否存在然后搜索正确的位置,你可以直接寻找位置。 (您不需要搜索两次是您在本练习中应该做出的发现之一。)

我会(因为您可能不应该使用 &lt;algorithm&gt; 中的任何功能或此类“高级”功能)

当你到达终点时打破循环当你找到该项目时, 如果您发现一个大于该项目的元素,您应该在该位置之前插入并停止。 幸运的是,insert 需要在之前插入一个迭代器,因此您可以使用 i

类似这样的:

for (vector<string>::iterator i = v.begin(); i != v.end() && *i != temp; ++i)

    if (*i > temp)
    
        v.insert(i, temp);
        break;
    

注意break 表示i 不用于insert 之后的任何比较,因此插入是安全的。

【讨论】:

我不知道修改向量无效向量。即使我在 push_back 之后中断(在 k 循环下),那仍然不允许吗? 这只是打破了内循环——外循环也有一个在向量上的迭代器,这就是问题所在。【参考方案3】:

如前所述,您可以使用std::set 来存储您唯一的单词。你可以这样填充它:

std::set<std::string> set_of_words(std::ifstream & input)

  std::set<std::string> words;

  std::string word;
  while (input >> word)
  
    words.insert(word);
  

  return words;

或者您可以在您的问题中使用std::vector。使用来自&lt;algorithm&gt;std::lower_bound 你可以这样使用它:

std::vector<std::string> vector_of_words(std::ifstream & input)

  std::vector<std::string> words;

  std::string word;
  while (input >> word)
  
    auto pos = std::lower_bound(words.begin(), words.end(), word);
    if (pos == words.end())
    
      words.push_back(word);
    
    else
    
      if (*pos != word)
      
        words.insert(pos, word);
      
    
  

  return words;

【讨论】:

以上是关于在某些循环内使用 vector.push_back 时出现分段错误的主要内容,如果未能解决你的问题,请参考以下文章

正确使用用户定义类型的 std::vector.push_back()

在展开期间将向量成员推入向量:vector.push_back(vector[0])

vector::push_back 和 string::push_back 之间的区别?

Vector::push_back() 给出读取访问冲突

vector.push_back() 方法调用在 C++ 中不起作用

std::vector.push_back() 的奇怪(记忆?)问题