递归 - 分段错误(核心转储)

时间:2018-01-09 19:24:44

标签: c

所以我正在编写一个代码作为递归的练习。其目的是通过递归替换具有最大元素的数组的最后一个元素,按升序对整数数组进行排序。我不断得到分段错误错误,无法找出问题所在。这是代码。

#include <stdio.h>

#define N 10

void selection_sort(int array[], int length);

int main (void)
{
    int array[N];
    int i;

    printf("Enter a series of integrs: ");
    for(i = 0; i < N; i ++)
    {
        scanf("%d", &array[i]);
    }

    selection_sort(array, N);

    printf("In sorted order: ");
    for(i = 0; i < N; i ++)
    {
        printf("%d ", array[i]);
    }
    printf("\n");

    return 0;
}

void selection_sort(int array[], int length)
{
    int max = array[0];
    int temp;
    int i;
    int maxPlace;

    if (length < 2)
    {
        return;
    }

    for (i = 1; i < length; i ++)
    {
        if (array[i] > max)
        {
            max = array[i];
            maxPlace = i;
        }
    }
    temp = array[length - 1];
    array[length - 1] = max;
    array[maxPlace] = temp;

    selection_sort(array, length - 1);
}

1 个答案:

答案 0 :(得分:4)

因为maxPlace可能未初始化。而且因为您输入的输入不正确,所以即使用户只输入1,您也总会得到N个元素。

这是说明我如何追踪这一点的说明。通常情况下,我会使用Valgrind跟踪一个段错误,它会告诉我有问题的行,但是目前在OS X上已经破坏了。所以这是打印和演绎的老式方式。

首先,如果您离开已分配内存的末尾,-fsanitize=address编译器标志将导致运行时失败,例如使用未初始化的变量。这通常可以在发生时记录错误,而不是在路上发生。它还会告诉你它发生了什么功能。

$ make
cc -fsanitize=address -Wall -Wshadow -Wwrite-strings -Wextra -Wconversion -std=c99 -pedantic -g `pkg-config --cflags glib-2.0`   -c -o test.o test.c
cc `pkg-config --libs glib-2.0` -lssl -lcrypto -fsanitize=address  test.o   -o test

$ ./test
Enter a series of integrs: 5
ASAN:DEADLYSIGNAL
=================================================================
==80879==ERROR: AddressSanitizer: SEGV on unknown address 0x7ffee6679358 (pc 0x0001095a7cac bp 0x7ffee6659330 sp 0x7ffee66592b0 T0)
    #0 0x1095a7cab in selection_sort (/Users/schwern/tmp/./test+0x100001cab)
    #1 0x1095a78db in main (/Users/schwern/tmp/./test+0x1000018db)
    #2 0x7fff73819114 in start (/usr/lib/system/libdyld.dylib+0x1114)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/Users/schwern/tmp/./test+0x100001cab) in selection_sort
==80879==ABORTING
Abort trap: 6

现在我知道它在selection_sort()。我可以开始添加调试打印语句来缩小范围。那个for循环是可疑的,也许你正在离开阵列,所以我从那开始。我也想知道你是否有进攻,所以我已经打印出来了。

puts("Entering loop");
for (i = 1; i < length; i ++)
{
    printf("i: %d\n", i);
    if (array[i] > max)
    {
        max = array[i];
        maxPlace = i;
    }
    puts("Loop next");
}
puts("Leaving loop");

temp = array[length - 1];
array[length - 1] = max;
array[maxPlace] = temp;

puts("Recursing");
selection_sort(array, length - 1);

试试......

$ ./test
Enter a series of integrs: 5
Entering loop
i: 1
Loop next
i: 2
Loop next
i: 3
Loop next
i: 4
Loop next
i: 5
Loop next
i: 6
Loop next
i: 7
Loop next
i: 8
Loop next
i: 9
Loop next
Leaving loop
ASAN:DEADLYSIGNAL

不,你没有走出阵列。但你也没有递归。所以它必须在循环之后的几行代码中。好吧,标记那些。没什么好看的,当范围很小时,数字很好。

puts("1");
temp = array[length - 1];
puts("2");
array[length - 1] = max;
puts("3");
array[maxPlace] = temp;

运行那个......

$ ./test
Enter a series of integrs: 5
Entering loop
i: 1
Loop next
i: 2
Loop next
i: 3
Loop next
i: 4
Loop next
i: 5
Loop next
i: 6
Loop next
i: 7
Loop next
i: 8
Loop next
i: 9
Loop next
Leaving loop
1
2
3
ASAN:DEADLYSIGNAL

现在我知道它在3之后和recursing之前。这意味着它是array[maxPlace] = temp;。好的,maxPlace中的内容是什么?

puts("1");
temp = array[length - 1];
puts("2");
array[length - 1] = max;
printf("maxPlace: %d\n", maxPlace);
array[maxPlace] = temp;

运行那个......

$ ./test
Enter a series of integrs: 5
Entering loop
i: 1
Loop next
i: 2
Loop next
i: 3
Loop next
i: 4
Loop next
i: 5
Loop next
i: 6
Loop next
i: 7
Loop next
i: 8
Loop next
i: 9
Loop next
Leaving loop
1
2
maxPlace: 32766
ASAN:DEADLYSIGNAL

maxPlace是32766意味着它可能包含垃圾。现在我知道什么变量是问题我可以看看它是如何声明,初始化和设置发现它永远不会被初始化的情况:如果列表按降序排序。

但我只输了一个数字,5! if (length < 2)不应该抓住那个吗?不,因为无论您阅读了多少输入,您总是在N元素上进行迭代。

printf("Enter a series of integrs: ");
for(i = 0; i < N; i ++)
{
    scanf("%d", &array[i]);
}

如果我输入5然后点击ctrl-d结束输入,则scanf只会设置array[0] = 5。但循环将继续以1到9运行。scanf将失败,因为stdin已关闭。数组的其余部分将是未初始化的。

您正在排序N个元素,而不是读取的数字。因此,无论是否初始化,您都将对10个元素进行排序。

你很幸运他们碰巧是0。

$ ./test
Enter a series of integrs: 5
In sorted order: 0 0 0 0 0 0 0 0 0 5 

相反,您应该阅读N或直到scanf失败,以先到者为准。这也可以保护您免受垃圾输入。并且只能对读取的整数进行排序和打印,而不是数组的最大容量。

int main (void)
{
    int array[N];
    int num_ints;

    printf("Enter a series of integrs: ");
    for(num_ints = 0; num_ints < N; num_ints++)
    {
        if( scanf("%d", &array[num_ints]) < 1 ) {
            break;
        }
    }

    selection_sort(array, num_ints);

    printf("In sorted order: ");
    for(int i = 0; i < num_ints; i ++)
    {
        printf("%d ", array[i]);
    }
    printf("\n");

    return 0;
}