【问题标题】:Why do I get a segmentation fault while iterating through this vector?为什么在遍历此向量时会出现分段错误?
【发布时间】:2011-10-01 22:51:53
【问题描述】:

我需要遍历这个向量并删除重复项。此代码中某处发生分段错误。我的猜测是它与在迭代器运行时删除元素有关,但我对这些迭代器实际上是如何工作的还没有具体的了解,所以我无法弄清楚。

vector<char *>::iterator iter;
for (iter = v->begin(); iter != v->end()-1; iter++ ){
    char *aString = *iter;
    int n = 1;
    while(iter+n != v->end()){
        int comparison = strcmp(aString, *(iter+n));
        if(comparison == 0){
            v->erase(iter + n);
        }
        n++;
    }
}

【问题讨论】:

  • 别猜了。在调试器中运行它并找出导致段错误的原因。
  • 向量绝不是空的,是吗?
  • @weezybizzle - 看看我的回答,你的代码中确实只有几个小的错误。

标签: c++ vector iterator std


【解决方案1】:

说真的,您这里只是遇到了一对一的问题。当您删除一个元素时,您与 end() 进行了错误的比较并增加了 n

for (iter = v->begin(); iter != v->end()-1; iter++ ){
                                  ^^^^^^^^

while(iter+n != v->end())
             ^^

以下将做你想做的事(并证明它有效):

int main()
{

    std::vector<const char*> v (4, "this");
    std::vector<const char *>::iterator iter;

    for (iter = v.begin(); iter <  v.end(); iter++ ) {
        std::cout << *iter << " ";
    }
    std::cout << std::endl;

    for (iter = v.begin(); iter <  v.end(); iter++ ){
        const char *aString = *iter;
        int n = 1;
        while(iter+n < v.end()){
            int comparison = strcmp(aString, *(iter+n));
            if(comparison == 0){
                v.erase(iter + n);
            }
            else
                n++;
        }
    }

    for (iter = v.begin(); iter <  v.end(); iter++ ) {
        std::cout << *iter << std::endl;
    }

}

输出是:

这个这个这个这个
这个

【讨论】:

  • 这对我有用,非常感谢。那么 v.end() 是否指向向量中的最后一个元素?还是有点像字符串中的空终止符,只是一个指定结尾的元素?
  • @weezybizzle - vector.end() 返回一个迭代器,它引用向量容器中的 past-the-end 元素,而不是最后一个元素。
  • 您的iter != v.end()-1 将在向量中由于擦除而只剩下一个元素时出现段错误,然后迭代器增加到v.end()。你可以写iter &lt;= v.end()-1,但这有点难看; iter &lt; v.end() 是正常的写法。
  • 好的,这很有意义。非常感谢@BrianRoach
【解决方案2】:

您没有正确地遍历向量的其余部分。 Beta 建议的替代方法是将erase-remove 与remove_if 一起使用。像这样:

bool compare_strings(char * aString,char * bString)
{
    return 0==strcmp(aString,bString);
}

void remove_duplicates(vector<char *> * v)
{
    vector<char *>::iterator iter;
    for (iter = v->begin(); iter != v->end(); iter++ ) {
        v->erase(std::remove_if(iter+1,v->end(),compare_strings),v->end());
    }
}

【讨论】:

    【解决方案3】:

    当你从向量中删除一个元素时,向量会缩短一个元素。试试这个:

    if(comparison == 0){
      v->erase(iter + n);
    }
    else{
      n++;
    }
    

    【讨论】:

    • 仍然出现分段错误
    【解决方案4】:

    从向量中擦除会使从被擦除者开始的所有迭代器都无效,因此您可能不应该按照您的方式构建循环,而应该使用这样的标准习语:

    for (auto it = v.begin(); it != v.end(); ++it)   // no end() - 1 -- may not be legal!
    {
      for (auto jt = it; jt != v.end(); )
      {
        if (jt == it) continue;
    
        if (strcmp(*it, *jt) == 0)
        {
          jt = v.erase(jt);
        }
        else
        {
          ++jt;
        }
      }
    }
    

    此代码避免了对空向量的检查,您的代码未能考虑到这一点。

    【讨论】:

    • 他没有删除迭代器引用的元素,因此它不会失效(或者我错过了什么?)。
    • 你需要将n初始化为1,否则它会将字符串与自身进行比较。
    • @IronMensan:已修复!请注意,我坚持从零开始,这样我们就不需要额外的非空检查。
    • @KerrekSB - 请检查您的答案;他没有使他的迭代器无效。他只是有几个问题。
    • 我不明白您对使用strncmp 的评论。为什么向量内的char *s 会指向向量本身? std::distance(it + n, v.end())char *s 指向的内存有什么关系?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-25
    • 1970-01-01
    • 1970-01-01
    • 2017-11-24
    • 2021-07-30
    相关资源
    最近更新 更多