简体   繁体   English

为什么向量迭代器指向超出边界?

[英]Why does the vector iterator point out of bounds?

I'm studying a recursive mergesort algorithm, and one iterator goes out of bounds. 我正在研究递归mergesort算法,并且一个迭代器超出范围。 I'm positive the root of my problem is that my algorithm is flawed, but I've spent days pouring over it, and I just don't see my misstep. 我很肯定我的问题的根源是我的算法存在缺陷,但是我已经花了几天时间倾倒它,我只是没有看到我的错误。 I don't know what direction to take. 我不知道要采取什么方向。 Can someone more experienced/smarter than I take a look? 有人比我看一看更有经验/更聪明吗? (Full program with driver is available on Github here .) (在这里 ,Github上提供了带驱动程序的完整程序。)

Output is: 输出是:

before: 50 5 40 10 30 15 20 20 10 25 
after : -1808873259 5 10 10 15 20 20 25 30 40 50 
/*      ^  
 *      Extra recursive call, and out-of-bounds.
 */

To be clear, I am constrained to returning a vector of type T, in this case int, but I'm aware from this post that using a void function is better. 为了清楚起见,我被约束为返回类型为T的向量,在本例中为int,但我从这篇文章中知道使用void函数更好。

template <typename T>
vector<T> mergesort(typename vector<T>::iterator begin, typename vector<T>::iterator end){
    vector<T> newVector;
    if (begin!=end){
        vector<T> tmp1;
        vector<T> tmp2;
        typename vector<T>::iterator mid1 = begin;
        typename vector<T>::iterator mid2 = begin;

        long origDistance = distance(begin,end);
        long endOfRange1 = origDistance/2;
        long begOfRange2 = endOfRange1+1;

        advance(mid1,endOfRange1);
        advance(mid2,begOfRange2);

        tmp1 = mergesort<T>(begin,mid1);
        tmp2 = mergesort<T>(mid2,end);

        //"merge()" is from the STL, link in comments. 
        merge(tmp1.begin(),tmp1.end(),tmp2.begin(),tmp2.end(), back_inserter(newVector));

    } else {
        newVector.push_back(*begin);
    }
    return newVector;
}

You dereference begin when begin == end . 取消引用beginbegin == end That's undefined behavior. 这是未定义的行为。 Probably you want if (origDistance == 1) then push_back the single element and return. 可能你想要if (origDistance == 1)然后push_back单个元素并返回。

Your function looks like it could work, if end points to the last element of the vector. 如果end指向向量的最后一个元素,那么你的函数看起来可以工作。 However in your sample program you call it like this: 但是在您的示例程序中,您可以这样称呼它:

 newVector = mergesort<int>(vec.begin(), vec.end()); 

The vec.end() points past the end of the vector, it doesn't point to the last element. vec.end()指向矢量的末尾,它不指向最后一个元素。 So your function messes up because it ultimately tries to access the element pointed to by the second iterator you pass in. 因此,您的函数会混乱,因为它最终会尝试访问您传入的第二个迭代器所指向的元素。

You could call your function like: mergesort<int>(vec.begin(), vec.end() - 1); 可以调用你的函数: mergesort<int>(vec.begin(), vec.end() - 1); .

However this will surprise anyone else reading your code. 但是,这会让其他人阅读您的代码感到惊讶。 It would be much better to rewrite your mergesort function to follow normal C++ range conventions, that is, the parameter named end should be past-the-end. 重写mergesort函数以遵循正常的C ++范围约定会更好,也就是说,名为end的参数应该是past-the-end。 mid1 should equal mid2 . mid1应该等于mid2

Okay - couldn't go to sleep without figuring this out, and huge credit goes to John Zwinck and MM for getting me in the right direction - here's the code which got the correct output: 好吧 - 如果不把它搞清楚就无法入睡,并且John ZwinckMM为了让我朝着正确的方向而获得了巨大的荣誉 - 这是获得正确输出的代码:

template <typename T>
vector<T> mergesort(typename vector<T>::iterator begin, typename vector<T>::iterator end){
    vector<T> newVector;
    long origDistance = distance(begin,end); /*Get distance first.*/

    if (origDistance==1){ /*Added better anchor case checking for distance.*/
        newVector.push_back(*begin);
        return newVector;
    }

    vector<T> tmp1;
    vector<T> tmp2;
    typename vector<T>::iterator mid1 = begin;
    typename vector<T>::iterator mid2 = begin;

    long endOfRange1 = origDistance/2;
    long begOfRange2 = endOfRange1;/*Edited from: endOfRange+1*/

    advance(mid1,endOfRange1);
    advance(mid2,begOfRange2);

    tmp1 = mergesort<T>(begin,mid1);
    tmp2 = mergesort<T>(mid2,end);

    merge(tmp1.begin(),tmp1.end(),tmp2.begin(),tmp2.end(), back_inserter(newVector));
        return newVector;
}

Here I will show you how to do it. 在这里,我将向您展示如何做到这一点。

template <typename T>
void mergesort(typename vector<T>::iterator, typename vector<T>::iterator);

// ...

    mergesort<int>(vec.begin(), vec.end());
    newVector = vec;

// ...

template <typename T>
void mergesort(typename vector<T>::iterator begin, typename vector<T>::iterator end){
    auto const N = std::distance(begin, end);
    if (N <= 1) return;                   
    auto const middle = std::next(begin, N / 2);
    mergesort<T>(begin, middle);
    mergesort<T>(middle, end);
    std::inplace_merge(begin, middle, end); 
}

Correct output: 正确输出:

before: 50 5 40 10 30 15 20 20 10 25 
after : 5 10 10 15 20 20 25 30 40 50 

STL already has inplace_merge so why reimplement it? STL已经有inplace_merge,为什么要重新实现呢? With this approach you don't have to think has hard with boundaries. 通过这种方法,您不必认为边界很难。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM