C 虽然有足够的可用内存,但堆栈溢出

C 虽然有足够的可用内存,但堆栈溢出,c,arrays,recursion,quicksort,stack-overflow,C,Arrays,Recursion,Quicksort,Stack Overflow,在我的课堂上,我们的老师想让我们用C语言编写一个快速排序算法,它可以处理10000个int数组。我和我的朋友已经编写了如上所示的代码 对随机值数组进行排序时,它可以正常工作,但对排序后的数组进行排序时,代码崩溃,如图所示 Unhandled exception at 0x00FF2509 in ConsoleApplication1.exe: 0xC0000005: Access violation writing location 0x00330F58. 现在我搜索了一下,发现linker为

在我的课堂上,我们的老师想让我们用C语言编写一个快速排序算法,它可以处理10000个
int
数组。我和我的朋友已经编写了如上所示的代码

对随机值数组进行排序时,它可以正常工作,但对排序后的数组进行排序时,代码崩溃,如图所示

Unhandled exception at 0x00FF2509 in ConsoleApplication1.exe: 0xC0000005: Access violation writing location 0x00330F58.
现在我搜索了一下,发现linker为递归函数提供了1MB的堆栈(尽管我可能弄错了)。因此,对于4字节整数变量(当再次对排序数组进行排序时,其中的4x10k变量)和10k整数引用数组,所有这些变量都应该占用大约200KB的堆栈

所以我不知道为什么会出现这个错误。老师告诉我们(当然)可以把那个代码写成伪代码。所以要么我在代码上做错了什么,要么我不知道

谁能帮我解释一下怎么了

void quicksort_last(int *A,int p,int r){

if(p<r){
    int q=partition(A,p,r);
    quicksort_last(A,p,q-1);
    quicksort_last(A,q+1,r);
}
}

int partition(int *A,int p, int r){
const int x=A[r];
int i=p-1;
int j=p;
int temp;

while(j<r){
    if(A[j]<=x){
        i++;
        temp=A[i];
        A[i]=A[j];
        A[j]=temp;
    }
    j++;
}
temp=A[i+1];
A[i+1]=A[r];
A[r]=temp;
return i+1;
}
void quicksort\u last(int*A、int p、int r){

如果(p如果您的异常状态为访问冲突,您为什么会考虑堆栈限制?您的代码中肯定存在一个错误,导致您写入不属于应用程序的禁止内存。您是否在调试器中运行?它至少会告诉您错误发生在哪个迭代中。

概要 您对算法的转录和实现似乎还不错;它对我来说毫无问题

您需要仔细检查驱动程序代码,以确保没有超出任何数组的界限。很可能您会在该驱动程序代码中发现问题

分析 在使用了您展示的排序和分区代码之后,我认为它没有什么大问题。我使用了以下测试用例,但在Mac OS X 10.9上使用GCC 4.8.2进行了测试。它是使用
-std=c11
编译的(在其他选项中),并使用添加到C99中的两个功能—“在<代码>中声明变量,用于<代码>循环”功能和“在需要时声明变量”功能。如果不使用支持C99的编译器,可以直接修复这些功能

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

static void dump_partition(char const *tag, int *array, int lo, int hi)
{
    if (lo < hi)
    {
        int i;
        printf("%s: %d..%d\n", tag, lo, hi);
        for (i = lo; i <= hi; i++)
        {
            printf(" %4d", array[i]);
            if ((i - lo) % 10 == 9)
                putchar('\n');
        }
        if ((i - lo) % 10 != 0)
            putchar('\n');
    }
}

static int partition(int *A, int p, int r)
{
    const int x=A[r];
    int i=p-1;
    int j=p;
    int temp;

    while (j < r)
    {
        if (A[j] <= x)
        {
            i++;
            temp=A[i];
            A[i]=A[j];
            A[j]=temp;
        }
        j++;
    }
    temp=A[i+1];
    A[i+1]=A[r];
    A[r]=temp;
    return i+1;
}

static void quicksort_last(int *A, int p, int r)
{
    if (p < r)
    {
        int q=partition(A, p, r);
        printf("quicksort: %p (%d..%d)\n", (void *)&q, p, r);
        //dump_partition("L-part", A, p, q-1);
        //dump_partition("R-part", A, q+1, r);
        quicksort_last(A, p, q-1);
        quicksort_last(A, q+1, r);
    }
}

int main(void)
{
    int data[] =
    {
        31, 14, 53, 45, 88,  0, 79, 59, 84,  5,
        83, 42, 61, 38, 24, 47, 86, 69,  8, 36,
    };
    enum { N_DATA = sizeof(data) / sizeof(data[0]) };

    dump_partition("Random", data, 0, N_DATA-1);
    quicksort_last(data, 0, N_DATA-1);
    dump_partition("Sorted", data, 0, N_DATA-1);

    enum { BIG_SIZE = 10000 };
    int data2[BIG_SIZE];
    srand(time(0));
    for (int i = 0; i < BIG_SIZE; i++)
        data2[i] = rand() % BIG_SIZE;

    dump_partition("Random", data2, 0, BIG_SIZE-1);
    quicksort_last(data2, 0, BIG_SIZE-1);
    dump_partition("Sorted", data2, 0, BIG_SIZE-1);

    return 0;
}
是:

最大堆叠深度为:

  0x7fff555f6e30
- 0x7fff555f66f0
  --------------
  0x000000000740
它小于2 KiB。堆栈上有28个级别。这是随机数据(显示的代码)。当我修改代码对已经排序的数据进行排序时,使用的堆栈要大得多-正如我在注释中所指出的,这导致分区中出现非常糟糕的行为

如果您的输入数据已经排序,选择子数组的第一个元素作为轴值将导致二次排序和非常深入的递归。最好随机选择轴,或使用或相关技术

堆栈上有9999个级别,堆栈位置的差异为:

  0x7fff5d0bde30
- 0x7fff5d021ab0
  --------------
  0x000000013074
然而,使用的堆栈空间仍然不到80kib(在排序代码中;数组的空间是额外的,但这是一个已知的数量,大约为40kib)。所有这些大小都不应该给普通机器带来压力

因此,我必须诊断出问题并不是您在问题中显示的代码。结果往往是令人惊讶的

您可以通过获取我的驱动程序代码(
main()
dump\u分区()
函数)并使用
quicksort\u last()运行它来验证这一点
。你应该会发现与我得到的结果类似的结果。如果是这样,你可以开始编写你的驱动程序代码。我看了一下,决定不仔细检查它-事实上,最初发布的代码根本不适合我。它似乎也有大量重复代码,这总是一个坏迹象。当我我做了足够多的工作来编译免警告(这意味着在很大程度上打印出仔细计算的时间,但也有其他问题),然后我得到的输出是:

 0.000787
 0.156366
 0.000001
 0.031464
 0.000001
 0.040427
 0.001198
 0.597619
 0.000001
 0.121826
 0.000000
 0.159727
 0.001914
 1.335059
 0.000001
 0.275667
 0.000002
 0.358740
 0.002504
 2.381662
 0.000000
 0.487816
 0.000000
 0.645867

这是使用
%13.6f
作为格式字符串打印时间。同样,它在Mac OS X 10.9上没有崩溃。我没有在这段代码中测量堆栈使用情况。

好吧,我做的最后一件事是我应该做的第一件事(像往常一样)

当我运行这段代码时,它会出现大约4700次递归的裂缝。因此,我得到的结果是,要么是1MB堆栈被填充到4700次深度,要么是有限制(我对此表示怀疑)


感谢您抽出时间帮助大家;)

不要介意.c扩展名关于10k大小的数组:)请正确缩进您的代码我通常会这样做,但由于某些原因,它与我最初做的缩进不同。如果您看上面,您可以看到原始代码。顺便说一句,它不是很可读,我知道,但这些代码是用有限的格式编写的time@umurcan这是个站不住脚的借口大多数人le在这里也有有限的时间。对他们(和你自己)友好,这样他们可能会对你友好。你能将主函数中的for循环更改为;
for(int i=0;i
然后告诉我发生了什么?我知道当数字是随机的时,快速排序会更好,但我必须让它在最坏的情况下工作:)顺便说一句,我想感谢你的回答。这一定花了一些时间,而且肯定给了我一个新的视角:)顺便说一句,当我按照我的要求做时,我忘了告诉你,程序崩溃了当我重新排序已经排序的数据时,我有效地做到了这一点。然而,我也按照要求做了,代码对我来说运行良好,但我对使用的堆栈空间量有不同的度量。原始数据是
0x7fff51ba3e30-0x7fff51b07ab0=0x9C380=639872
(约625 KiB),这比我昨天确定的要大得多。我必须重新运行原始代码,以检查我昨天是否搞错了。
  0x7fff5d0bde30
- 0x7fff5d021ab0
  --------------
  0x000000013074
 0.000787
 0.156366
 0.000001
 0.031464
 0.000001
 0.040427
 0.001198
 0.597619
 0.000001
 0.121826
 0.000000
 0.159727
 0.001914
 1.335059
 0.000001
 0.275667
 0.000002
 0.358740
 0.002504
 2.381662
 0.000000
 0.487816
 0.000000
 0.645867
void plusplus(int a){
    printf("%d\n",a);
    plusplus(a+1);
}

int main(){
    plusplus(0);
    return 0;
}