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

Segmentation fault when using vector.push_back inside of certain loops

本文关键字:back 错误 分段 push vector 循环      更新时间:2023-10-16

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

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

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

以下是我的代码片段:

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,它会出现分段错误。我完全被难住了。

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

感谢您提前提供的帮助!

编辑1:我应该提到我在VS 2019中测试过它。矢量库文件弹出,说明引发了"读取访问冲突"异常。没有分段故障(或者这可能是VS告诉我发生了分段故障的方式?(

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

编辑3:我只允许使用向量,不允许使用集合或其他容器。如果我能用一套,我会的。

当您修改向量迭代器时,它将无效。

原因有两个:

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

有一种快速的方法可以修复它。当你进行修改时,你必须获取迭代器的更新值。poush_back没有这样的功能,但std::vector::insert将迭代器返回到新值,该迭代器可以用于更新循环迭代器。

我可以修复你的代码,但它太复杂了(缩进太多(,我希望避免这种情况。您应该首先将此代码分割为较小的函数。

相反,挽救你的代码,这是我的版本:

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

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

但你做得太复杂了——因为向量是有序的,你不需要先看看字符串是否存在,然后搜索正确的位置,你可以直接寻找位置
(你不需要搜索两次,这是你在这个练习中应该做出的发现之一。(

我会(因为你可能不应该使用<algorithm>的任何功能或类似的"高级"功能(

  • 当你到达终点时打破循环或当你找到物品时
  • 如果发现一个元素大于该项,则应在该位置之前插入并停止
    幸运的是,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之后的任何比较,因此插入是安全的。

如前所述,您可以使用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。使用<algorithm>中的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;
}