排序功能实现中的分段错误

Posted

技术标签:

【中文标题】排序功能实现中的分段错误【英文标题】:Segmentation Fault in the implementation of a sorting function 【发布时间】:2020-01-27 19:05:21 【问题描述】:

我正在尝试基于为我的编程课程提供的伪代码实现排序算法,而我和我的合作伙伴一直在遇到(核心转储)错误,通常是分段错误。我知道这通常意味着程序正在尝试访问它不允许访问的内存,但我不确定如何解决这个问题。

#include <iostream>
#include <cmath>

using namespace std;

void FlipFlopSort(int *Array, int begin, int end)
    int length = (end-begin);
    float midh = 2*(float)length/3;
    midh = ceil(midh);
    float midl = (float)length/3;
    midl = ceil(midl);
    //cout << "size of sorted area = " << length << endl;
    if(length<=2)
        //cout << "Length = 2" << endl;
        if(Array[begin] > Array[begin+1])
            swap(Array[begin], Array[begin+1]);
        
    else
        //cout << "Recursion Begin 1" << endl;
        FlipFlopSort(Array, begin, midh);
        //cout << "Recursion End" << endl;
        FlipFlopSort(Array, midl, end);
        //cout << "Recursion Begin 2" << endl;
        FlipFlopSort(Array, begin, midh);
    


int main()
    // Declare Variables and Read Inputs
    int n;
    cin >> n;

    int Array[n];

    for(int i = 0; i < n; i++)
        cin >> Array[i];
    

    FlipFlopSort(Array, 0, n);


    for(int i = 0; i<n; i++)
         if(i != (n-1))
             cout << Array[i] << " ";
         else
             cout << Array[i] << endl;
         
    

    return 0;





【问题讨论】:

是时候启动调试器并调试您的代码了。这是了解正在发生的事情的最佳方式。 Array[n] 在 C++ 中也无效,即使某些编译器扩展支持它。使用正确的容器std::vector 你有没有机会使用 code:blocks 并且没有像样的调试信息? 我刚刚尝试调试我的程序并收到一条新消息,但我不太明白:它指出 std::ceil 中存在分段错误 0x0000555555554d35。然后继续说我无法在 /usr/include/c++/7/cmath:166 访问内存 这是您向下查看堆栈以查找代码中哪里出错的地方。 在您的调试器中应该有一种方法可以将调用堆栈向上传递到您的代码。当您编写代码时,它应该向您显示崩溃发生时程序的哪一行正在运行。在 Visual Studio 中,调试工具栏上有一个Stack Frame 组合框和一个调用堆栈窗口/视图。这两者都允许您向上/切换调用堆栈。 【参考方案1】:

让我们开始吧:

    int n;
    cin >> n;

    int Array[n];
    为什么 n 是有符号类型?数组大小不能声明为有符号类型。 C++ 严格禁止在堆栈上声明可变大小的数组(即您所做的方式)。它也是 C 的一个已弃用的特性。您应该改为:
    int* Array = new int[n];

您可能会遇到分段错误的原因之一是以下行:

    if (length <= 2) 
        //cout << "Length = 2" << endl;
        if (Array[begin] > Array[begin + 1]) 
            swap(Array[begin], Array[begin + 1]);
        
    
    如果长度小于或等于 2,是否意味着它至少有 2 个元素?它可能有 0 或 1。您应该在函数的开头添加一个检查:
void FlipFlopSort(int *Array, int begin, int end)
    int length = (end-begin);
    if (length <= 1) 
        return;
    
    ...

另一个原因,是因为您计算的是中间大小而不是索引,也就是说,您不向它们添加开始。

考虑:开始=100,结束=103。 -> length = 3, midl = 1 and midh = 2. 有意义吗?不!在这种情况下,这些不是有效的索引。你应该写

midh += begin;
midl += begin;

在他们的计算之后。

最后,你应该避免使用浮点数(除非你真的需要),所以我会写:

    const int midl = begin + length / 3;
    const int midh = begin + 2 * midl;

它不等同于你写的,但它仍然有效,而你的有风险(上限值是可疑的,因为你可能会发现自己超出了数组的末尾)。

void FlipFlopSort(int* Array, int begin, int end) 
    const int length = end - begin;
    if (length <= 1) 
        return;
    

    const int midh = begin + 2 * length / 3;
    const int midl = begin + length / 3;

    //cout << "size of sorted area = " << length << endl;
    if (length <= 2) 
        //cout << "Length = 2" << endl;
        if (Array[begin] > Array[begin + 1]) 
            swap(Array[begin], Array[begin + 1]);
        
    
    else 
        FlipFlopSort(Array, begin, midh);
        FlipFlopSort(Array, midl, end);
        FlipFlopSort(Array, begin, midh);
    

【讨论】:

我没有检查算法本身的正确性 我不能开始感谢你,我已经为此工作了好几天,一直无法找到解决方案,结果发现我没有意识到我需要添加开始是巨大的问题。再次,非常感谢您的帮助!到目前为止,我所做的每一个测试都有效,一旦我测试了所有案例,即使出现故障,从这里修复也应该更容易 @Michael ceil 操作是算法工作所必需的。不过也可以使用纯整数来完成。 @TedLyngmo 我刚刚在测试较大数量的集合时发现了这个问题,如果我不使用 ceil 算法会失败,如果我使用 ceil 则会出现分段错误。我被困在岩石和坚硬的地方之间【参考方案2】:

当您swap 时,您必须确保length 至少为 2,否则您将交换数字超出范围。您的 midlmidh 值当前是错误的。它们是相对于begin 的,因此您需要将begin 添加到它们。但是,您可以将 midl 添加到 Array 本身并跳过函数中的 begin 参数以简化界面。

我还将浮点 std::ceil 操作替换为整数版本。

// A function to do integer division and return the ceil value
size_t DivCeil(size_t dividend, size_t divisor) 
    return 1U + ((dividend - 1U) / divisor);


void FlipFlopSort(int* Array, size_t length) 
    if(length > 2) 
        // calculate midl & midh
        size_t midl = DivCeil(length, 3U);
        size_t midh = DivCeil(2U * length, 3U);

        FlipFlopSort(Array, midh);
        // add midl to Array and sub midl from length
        FlipFlopSort(Array + midl, length - midl);
        FlipFlopSort(Array, midh);
     else if(length == 2) 
        if(Array[1] < Array[0]) 
            // swap the values
            std::swap(Array[0], Array[1]);
        
     // else length < 2 ... don't do anything

#include <iostream>
#include <vector>

int main() 
    size_t n;
    if(std::cin >> n)  // check that the user entered a number
        // don't use VLA:s, use a std::vector instead
        std::vector<int> Array(n);

        for(size_t i = 0; i < Array.size(); ++i) 
            std::cin >> Array[i];
        

        FlipFlopSort(Array.data(), Array.size());

        for(int value : Array) 
            std::cout << value << '\n';
        
    

如果您希望您的排序算法更通用且可用于标准容器,您可以将输入参数替换为迭代器。

例子:

#include <algorithm> // std::iter_swap
#include <iterator>  // std::distance, std::next

// A function to do integer division and return the ceil value
template<typename T>
T DivCeil(T dividend, T divisor) 
    return 1 + ((dividend - 1) / divisor);


template<typename It>
void FlipFlopSort(It begin, It end) 
    auto length = std::distance(begin, end);     // iterator version of "length = end-begin"
    static constexpr decltype(length) two = 2;   // constant of the same type as length
    static constexpr decltype(length) three = 3; // -"-

    if(length > two) 
        // calculate midl & midh iterators
        auto midl = std::next(begin, DivCeil(length, three));
        auto midh = std::next(begin, DivCeil(two * length, three));

        FlipFlopSort(begin, midh);
        FlipFlopSort(midl, end);
        FlipFlopSort(begin, midh);
     else if(length == two) 
        if(*std::next(begin) < *begin) 
            // swap the values pointed at by the iterators
            std::iter_swap(begin, std::next(begin));
        
     // else length == 1 or 0 ... don't do anything

用法:

#include <iostream>
#include <vector>

int main() 
    size_t n;
    if(std::cin >> n) 
        std::vector<int> Array(n);

        for(size_t i = 0; i < Array.size(); ++i) 
            std::cin >> Array[i];
        

        FlipFlopSort(std::begin(Array), std::end(Array));

        for(int value : Array) 
            std::cout << value << '\n';
        
    

【讨论】:

以上是关于排序功能实现中的分段错误的主要内容,如果未能解决你的问题,请参考以下文章

快速排序的 C++ 实现中的运行时错误

链表中元素比较功能的实现

Sort() 导致分段错误

简单实现Redis缓存中的排序功能

js交换数组中2个元素的位置,实现排序功能

快速排序实现错误