排序时的分段错误

时间:2013-11-01 13:03:01

标签: c sorting pointers segmentation-fault

我已经实现了快速排序。但是这段代码给了我分段错误。

#include<stdio.h>
#include<stdlib.h>
void Quick_Sort(int *a,int p,int r);
int partition(int *a,int p,int r);
int main(){
    int testcase,num,search;
    int i,j,*array;
    scanf("%d",&testcase);
    for(i=0;i<testcase;i++){
        scanf("%d",&num);
        array=malloc(sizeof(int)*num);
        for(j=0;j<num;j++){
            scanf("%d",(array+j));
        }
        Quick_Sort(array,0,num-1);
        scanf("%d",&search);
        /*for(j=0;j<num;j++){
            if(search==*(array+j)){
                printf("%d\n",j+1 );
                break;
            }

        }*/
    }
    for(i=0;*(array+i)!='\0';++i){
        printf("%d ",*(array+i));
    }
}
void Quick_Sort(int *a,int p,int r){
    int q;
    if(p<r){
        q=partition(a,p,r);
        Quick_Sort(a,p,q);
        Quick_Sort(a,q+1,r);
    }
}
int partition(int *a,int p,int r){
    int i,j,x,temp;
    i=p-1;
    j=r+1;
    x=*(a+p);
    while(1){
        do{
        j=j-1;
    }
    while(x<=(*(a+j)));

    do{
        i=i+1;
    }
    while(x>=(*(a+i)));

    if(i<j){
        temp=*(a+i);
        *(a+i)=*(a+j);
        *(a+j)=temp;
    }else{
        return j;
    }
}

2 个答案:

答案 0 :(得分:2)

读取数据

您的数据文件似乎结构为:

T
N1 V11 V12 V13 ... V1N S1
N2 V21 V22 V23 ... V2N S2
...

你有一个测试用例的总数,T。然后对于每个测试用例,你在数组中有许多条目(N1,N2等),每次都跟着适当数量的值,V 1 1 .. V 1 N,以及您应该搜索的备用值(所有使用基于1的数组符号描述;在C中,您将使用基于0的数组)。虽然我已经在一行中显示了每个测试集的数据,但实际的数据文件可以按任意顺序排列数字 - 一切都可以在一行上,或者每行上的每个数字可能都是空白的分隔它们的线条,或这些格式的任何混合。

您展示的main()程序在阅读该数据时效果不佳。它缺乏所有错误检查。它使用(合法但)奇怪的*(array + i)符号而不是更简单的理解array[i]符号,显然相信它会更有效。当您使用指针提高效率时,在取消引用指针之前,不要继续向值添加i。你的代码动态分配内存,但永远不会释放它,泄漏可怕。

使用下标符号

读取数据

在这个修订过的代码中,我使用return 1;退出程序。它也应该打印错误信息,但我适度懒惰。

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

int main(void)
{
    int testcase, num, search;
    int i, j, *array;
    if (scanf("%d", &testcase) != 1)
        return 1;

    for (i = 0; i < testcase; i++)
    {
        if (scanf("%d", &num) != 1)
            return 1;
        array = malloc(sizeof(int)*num);
        if (array == 0)
            return 1;
        for (j = 0; j < num; j++)
        {
            if (scanf("%d", &array[j]) != 1)
                return 1;
        }
        //Quick_Sort(array, 0, num-1);
        if (scanf("%d", &search) != 1)
            return 1;
        for (j = 0; j < num; j++)
        {
            if (search == array[j])
            {
                printf("%d\n", j+1);
                break;
            }
        }
        // Print the array - best encapsulated in a small function
        for (j = 0; j < num; j++)
        {
            printf(" %d", array[j]);
            if (j % 10 == 9)
                putchar('\n');
        }
        if (j % 10 != 0)
            putchar('\n');
        // Prevent memory leaks
        free(array);
    }
    return 0;
}

我顺便提一下,无论QuickSort是否对数据进行排序,搜索循环都会起作用;它没有使用数组排序的事实。您可以在排序之前和排序之后打印数据。您应该标记输出以标识要打印的内容。例如,搜索代码可能会写:

printf("Found %d at %d\n", search, j);

您也无法确定何时找不到要搜索的值。

在您阅读之后以及处理之前打印您阅读的数据通常也是一个好主意,只是为了确保您的程序获得您希望获得的数据。如果程序没有处理您认为正在处理的数据,则可能会导致混淆。

请注意,此代码不会对数组中的值进行任何假设,超出“它们是有效整数”。它确实检查每个输入操作。虽然看似乏味,但有必要避免麻烦。

使用指针符号

读取数据

以下是使用指针或多或少惯用法的代码:

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

int main(void)
{
    int testcase;
    if (scanf("%d", &testcase) != 1)
        return 1;

    for (int i = 0; i < testcase; i++)
    {
        int num;
        if (scanf("%d", &num) != 1)
            return 1;
        int *array = malloc(sizeof(int)*num);
        if (array == 0)
            return 1;
        int *end = array + num;
        int *ptr;
        for (ptr = array; ptr < end; ptr++) 
        {
            if (scanf("%d", ptr) != 1)
                return 1;
        }

        //Quick_Sort(array, 0, num-1);

        int search;
        if (scanf("%d", &search) != 1)
            return 1;
        for (ptr = array; ptr < end; ptr++) 
        {
            if (search == *ptr)
            {
                break;
            }
        }
        if (ptr < end)
            printf("Found %d at %td\n", search, ptr - array + 1);
        else
            printf("Missing %d\n", search);

        // Print the array - best encapsulated in a small function
        printf("Array (%d):", num);
        int j;
        for (j = 0; j < num; j++)
        {
            printf(" %d", array[j]);
            if (j % 10 == 9)
                putchar('\n');
        }
        if (j % 10 != 0)
            putchar('\n');

        // Prevent memory leaks
        free(array);
    }
    return 0;
}

如果使用索引编写,则打印循环最简单,因此我没有将其更改为使用指针。可以这样做,但在'是时候打印换行符'代码中使用(ptr - array)而不是j会使它变得不那么值得。该代码使用C99功能,例如在需要时声明变量,在t中使用%td限定符作为ptrdiff_t值。可以编写它来使用VLA而不是malloc()

示例输入数据

3
2 1 2 1
3 3 2 0 1
4 5 4 3 2 4

示例输出

Found 1 at 1
Array (2): 1 2
Missing 1
Array (3): 3 2 0
Found 4 at 2
Array (4): 5 4 3 2

工作快速入门代码

您的分区算法有缺陷。它是固定的,标记了关键变化。我在解决问题时使用的调试脚手架留在原位,许多打印操作引导我注释掉。阅读Jon Bentley的Programming Pearls,特别是“第11栏:排序”(第11章,但章节最初是ACM通讯中的专栏,因此标题为第11栏)。在解决问题时,这是一个非常宝贵的指南。

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void Quick_Sort(int *a, int p, int r);
static int  partition(int *a, int p, int r);

static void dump_partition(char const *tag, int const *data, int lo, int hi);

/* Debugging functions */
static void check_sorted(int const *data, int lo, int hi);
static int *copy_partition(int const *data, int lo, int hi);
static void check_consistency(int const *a1, int const *a2, int lo, int hi);

int main(void)
{
    int testcase, num, search;
    int i, j, *array;
    if (scanf("%d", &testcase) != 1)
        return 1;

    for (i = 0; i < testcase; i++)
    {
        if (scanf("%d", &num) != 1)
            return 1;
        array = malloc(sizeof(int)*num);
        if (array == 0)
            return 1;
        for (j = 0; j < num; j++)
        {
            if (scanf("%d", &array[j]) != 1)
                return 1;
        }

        printf("\nData set %d:\n", i);
        int *copy = copy_partition(array, 0, num-1);
        dump_partition("Before:", array, 0, num-1);
        //dump_partition("Copy", copy, 0, num-1);
        Quick_Sort(array, 0, num-1);
        dump_partition("After: ", array, 0, num-1);
        check_sorted(array, 0, num-1);
        check_consistency(array, copy, 0, num-1);
        free(copy);

        if (scanf("%d", &search) != 1)
            return 1;
        for (j = 0; j < num; j++)
        {
            if (search == array[j])
                break;
        }
        if (j < num && search == array[j])
            printf("Found %d at %d\n", search, j+1);
        else
            printf("Missing %d\n", search);

        // Prevent memory leaks
        free(array);
    }
    return 0;
}

/* Although we're interested in data[lo]..data[hi], the copy must have data[0]..data[lo-1] too */
static int *copy_partition(int const *data, int lo, int hi)
{
    assert(lo <= hi);
    size_t nbytes = (hi + 1) * sizeof(int);
    int *space = (int *)malloc(nbytes);
    if (space == 0)
    {
        fputs("Out of memory\n", stderr);
        exit(1);
    }
    memmove(space, data, nbytes);
    return(space);
}

/* Check that the two arrays contain the same sets of data */
/* Each value in a1 must be present in a2 and vice versa */
static void check_consistency(int const *a1, int const *a2, int lo, int hi)
{
    int *a3 = copy_partition(a1, lo, hi);
    int  a3_lo = lo;
    int  a3_hi = hi;
    //printf("-->> check_consistency:\n");
    //dump_partition("a1", a1, lo, hi);
    //dump_partition("a2", a2, lo, hi);
    //dump_partition("a3", a3, lo, hi);
    for (int i = lo; i <= hi; i++)
    {
        int found = 0;
        for (int j = a3_lo; j <= a3_hi; j++)
        {
            if (a2[i] == a3[j])
            {
                /* Found a match for a2[i] at a3[j] */
                /* Move a3[j] to end of array and ignore it from here on */
                //printf("Found a2[%d] = %d at a3[%d] = %d\n", i, a2[i], j, a3[j]);
                int t = a3[a3_hi];
                a3[a3_hi] = a3[j];
                a3[j] = t;
                a3_hi--;
                //dump_partition("a3-free", a3, a3_lo, a3_hi);
                //dump_partition("a3-used", a3, a3_hi+1, hi);
                found = 1;
                break;
            }
        }
        if (!found)
        {
            printf("No value %d for a2[%d] in a1\n", a2[i], i);
            dump_partition("a2", a2, lo, hi);
            dump_partition("a1-free", a3, a3_lo, a3_hi);
            dump_partition("a1-used", a3, a3_hi+1, hi);
        }
    }
    free(a3);
    //printf("<<-- check_consistency\n");
}

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

static void check_sorted(int const *data, int lo, int hi)
{
    //printf("-->> check_sorted:\n");
    for (int i = lo; i < hi; i++)
    {
        if (data[i] > data[i+1])
            printf("Out of order: a[%d] = %d bigger than a[%d] = %d\n", i, data[i], i+1, data[i+1]);
    }
    //printf("<<-- check_sorted\n");
}

void Quick_Sort(int *a, int p, int r)
{
    if (p < r)
    {
        int q = partition(a, p, r);
        //dump_partition("Lo Range", a, p, q-1);
        //printf("Pivot: a[%d] = %d\n", q, a[q]);
        //dump_partition("Hi Range", a, q+1, r);
        Quick_Sort(a, p, q-1);          // JL: Optimization
        Quick_Sort(a, q+1, r);
    }
}

static int partition(int *a, int p, int r)
{
    assert(p <= r);
    if (p == r)                         // JL: Key change
        return p;                       // JL: Key change
    int i = p;                          // JL: Key change
    int j = r + 1;
    int x = a[p];
    //printf("-->> partition: lo = %d, hi = %d, pivot = %d\n", p, r, x);
    while (1)
    {
        do
        {
            j--;
            //printf("---- partition 1: a[%d] = %d\n", j, a[j]);
        }   while (x < a[j]);           // JL: Key change

        do
        {
            i++;
            //printf("---- partition 2: a[%d] = %d\n", i, a[i]);
        }   while (i <= r && x > a[i]); // JL: Key change

        if (i <= j)                     // JL: Key change
        {
            //printf("---- partition: swap a[%d] = %d with a[%d] = %d\n", i, a[i], j, a[j]);
            int temp = a[i];
            a[i] = a[j];
            a[j] = temp;
        }
        else
        {
            // This swap step is crucial.
            int temp = a[p];            // JL: Key change
            a[p] = a[j];                // JL: Key change
            a[j] = temp;                // JL: Key change
            //dump_partition("a-lo", a, p, j-1);
            //printf("a-pivot[%d] = %d\n", j, a[j]);
            //dump_partition("a-hi", a, j+1, r);
            //printf("<<-- partition: return j = %d; a[%d] = %d; (i = %d; a[%d] = %d)\n", j, j, a[j], i, i, a[i]);
            return j;
        }
    }
}

扩展样本输入

10

2 1 2 1
3 3 2 0 1
4 5 4 3 2 4
4 3 1 9 3 8
5 3 4 1 9 3 8
9 3 6 4 9 5 1 9 3 3 8
10 3 6 4 9 6 5 1 9 3 3 8

16
3 6 4 9 6 5 1 9 3 3
8 2 1 7 3 5
3

26
3 6 4 9 6 5 1 9 3 3
2 7 8 2 0 8 4 4 7 5
8 2 1 7 3 5
7

96
3 6 4 9 6 5 1 9 3 3
4 0 5 0 7 5 6 3 6 0
1 2 0 7 3 1 7 6 2 3
0 4 6 6 9 8 9 5 3 4
1 9 2 9 2 7 5 9 8 9
4 7 5 8 7 8 5 8 2 7
5 8 2 9 8 3 7 6 5 3
9 1 2 0 3 4 6 5 1 0
2 7 8 2 0 8 4 4 7 5
8 2 1 7 3 5
6

扩展样本输出

Data set 0:
Before: [0..1]  1  2
After:  [0..1]  1  2
Found 1 at 1

Data set 1:
Before: [0..2]  3  2  0
After:  [0..2]  0  2  3
Missing 1

Data set 2:
Before: [0..3]  5  4  3  2
After:  [0..3]  2  3  4  5
Found 4 at 3

Data set 3:
Before: [0..3]  3  1  9  3
After:  [0..3]  1  3  3  9
Missing 8

Data set 4:
Before: [0..4]  3  4  1  9  3
After:  [0..4]  1  3  3  4  9
Missing 8

Data set 5:
Before: [0..8]  3  6  4  9  5  1  9  3  3
After:  [0..8]  1  3  3  3  4  5  6  9  9
Missing 8

Data set 6:
Before: [0..9]  3  6  4  9  6  5  1  9  3  3
After:  [0..9]  1  3  3  3  4  5  6  6  9  9
Missing 8

Data set 7:
Before: [0..15]
  3  6  4  9  6  5  1  9  3  3
  8  2  1  7  3  5
After:  [0..15]
  1  1  2  3  3  3  3  4  5  5
  6  6  7  8  9  9
Found 3 at 4

Data set 8:
Before: [0..25]
  3  6  4  9  6  5  1  9  3  3
  2  7  8  2  0  8  4  4  7  5
  8  2  1  7  3  5
After:  [0..25]
  0  1  1  2  2  2  3  3  3  3
  4  4  4  5  5  5  6  6  7  7
  7  8  8  8  9  9
Found 7 at 19

Data set 9:
Before: [0..95]
  3  6  4  9  6  5  1  9  3  3
  4  0  5  0  7  5  6  3  6  0
  1  2  0  7  3  1  7  6  2  3
  0  4  6  6  9  8  9  5  3  4
  1  9  2  9  2  7  5  9  8  9
  4  7  5  8  7  8  5  8  2  7
  5  8  2  9  8  3  7  6  5  3
  9  1  2  0  3  4  6  5  1  0
  2  7  8  2  0  8  4  4  7  5
  8  2  1  7  3  5
After:  [0..95]
  0  0  0  0  0  0  0  0  1  1
  1  1  1  1  1  2  2  2  2  2
  2  2  2  2  2  3  3  3  3  3
  3  3  3  3  3  3  4  4  4  4
  4  4  4  4  5  5  5  5  5  5
  5  5  5  5  5  5  6  6  6  6
  6  6  6  6  6  7  7  7  7  7
  7  7  7  7  7  7  8  8  8  8
  8  8  8  8  8  8  9  9  9  9
  9  9  9  9  9  9
Found 6 at 57

还在一些更大的数据集(2097随机条目等)上测试了代码。当数据很大时,自动检查功能至关重要 - 因此check_sorted()check_consistency()。它们检查数据是按排序顺序输出的,还是保护属性,输入中的所有值都出现在输出中(就像它们在输入中出现的那样)。排序不应该添加新数据或删除预先存在的数据。

答案 1 :(得分:0)

正如评论中所指出的,打印循环的循环结束测试*(array + i) != '\0'是一个错误。 array保持正常整数,C也认为'\0'是整数0,因为它认为所有字符文字值都是整数,并且比较是合法的,但因为这是一个(希望)排序的整数数组可能有任何值(包括负值和0),可能是某些测试数据中有多个0,或者在这种情况下更可能是测试数据中没有0。如果你弄清楚这些代码在这些情况下会做什么,那么你应该理解错误。