【问题标题】:Modifying set while iterating gives me segfault迭代时修改集合给了我段错误
【发布时间】:2017-12-14 09:33:54
【问题描述】:

我目前正在研究一个使用int 集向量 的函数。 我希望我的函数 merge( ) 合并所有共享一个 int 的集合,所以例如我希望发生这种情况:

  [0]  -  0, 1, 2                              
  [1]  -  1, 3            Then it will              [0]  -  0, 1, 2, 3
  [2]  -  0, 3            output this vector ->     [1]  -  4, 5
  [3]  -  4, 5                                      [2]  -  6, 7, 8, 9
  [4]  -  6, 7, 8                                   
  [5]  -  8, 9  

这个函数我已经写好了,代码如下。 我几乎对每一行都进行了注释,这样我的代码就不会太难理解了!

// Merges all sets that shares at least one int
//
// PARAMETERS...
//      vectorE         : vector of sets of int
void mergeStates( std::vector< std::set< int > >& vectorE )
{
    // For every set of ints
    for( auto &currentSet : vectorE )
    {
        // For every ints of the set
        for( auto currentInt : currentSet )
        {   
            // The two for( ) loops down there allow me to iterate over 
            // every int of every set of the vectorE
            for( auto setToCheck : vectorE )
            {
                // If the set is different from the one we're already targeting
                if( currentSet != setToCheck )
                {
                    for( auto intToCheck : setToCheck )
                    {
                        // if we have found an int that is the same as the one we're targeting
                        if( intToCheck == currentInt )
                        {
                            // Merge
                            etatsetEtudie.insert( setToCheck.begin(), setToCheck.end() );

                            // Deleting the set we copied from, because we won't need it anymore
                            for(auto setToErase = vectorE.begin() ; setToErase != vectorE.end() ; ){
                                if( *setToErase == setToCheck )
                                    setToErase = vectorE.erase( setToErase );
                                else
                                    ++setToErase;
                            }
                        }
                    }
                }
            }
        }
    }
}

每次我运行我的程序时,在删除我们复制的集合时都会遇到段错误:我的错误在哪里?

编辑:我让它工作了!

好的,谢谢大家,我只是将参数设为 const 并添加了一个返回值,这样我就可以动态地将我需要的每个构造集合添加到一个新向量中,并返回这个向量:-)

【问题讨论】:

  • 如果我来找你,那是因为我真的找不到我的错误。
  • 您不能在迭代容器时对其进行修改。这是未定义的行为。
  • @Galik 通常不正确。每个容器都有自己的失效规则。
  • @Galik:您可以在迭代时从std::vector 中获取erase() - 只要您确保使用从erase() 返回的迭代器并且 进行比较更新的end()。但是,不方便的是,std::vector::erase() 的规范没有说明实际返回的内容......(参见 26.3.11.5 [vector.modifiers] 第 3 到 5 段)。事实证明,返回值的规范在 26.2.3 [sequence.reqmts] 第 11 段中。

标签: c++ vector segmentation-fault


【解决方案1】:

问题不是修改任何集合,而是修改向量。

从向量中删除某些内容会移动它之后的元素。首先,这意味着在擦除位置之后进入向量的迭代器(for-range 循环在内部使用迭代器)不再有效。其次,如果移动复制并覆盖了集合(而不是移动它们),那么您对集合的所有迭代器将不再有效。

结果是您的代码中有很多未定义的行为。

此外,即使该方法有效,您的最内层循环也不是擦除集合的好方法。这是非常非常低效的。

您至少需要重新考虑擦除元素的方式。但我认为提出一个总体上更好的算法会是更好的方法。

【讨论】:

    【解决方案2】:

    尝试制作一个新的向量而不是修改原来的向量:

    std::vector<std::set<int>> mergeStates(const std::vector<std::set<int>> & vectorE ) {
        std::vector<std::set<int>> new_vector;
    
        ...
    
        return new_vector;
    }
    

    【讨论】:

      【解决方案3】:

      您正在使用 使 迭代器无效的std::vector::erase 函数。因此,range based for loop 中的代码会尝试通过容器端访问迭代器。

      【讨论】:

        【解决方案4】:

        基于范围的for 使用的结束迭代器在循环之前确定。由于您在迭代期间erase(),因此最终实际上发生了变化。从 erase() 的结果中获取迭代器是不够的,因为 end 也发生了变化。我认为您可以不使用基于范围的 for 循环来删除您从中删除的范围。

        【讨论】:

          猜你喜欢
          • 2013-05-13
          • 1970-01-01
          • 2015-09-07
          • 2020-12-20
          • 2015-03-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多