简体   繁体   English

为什么此 Shaker Sort 代码在 C 中不起作用

[英]Why this Shaker Sort code doesn't work in C

I'm implementing a generic Shaker Sort algorithm in C and various websites present the code in a way that keeps on giving me segmentation faults and other errors, but it works just fine when using other languages.我正在用 C 实现一个通用的 Shaker Sort 算法,并且各种网站以一种不断给我分段错误和其他错误的方式呈现代码,但在使用其他语言时它工作得很好。 For example, this code has no issue if I keep it in C# but it stops working after adapting it to C.例如,如果我将其保留在 C# 中,则此代码没有问题,但在将其调整为 C 后它停止工作。

This is a full working example of my faithful adaptation of the aforementioned code:这是我忠实地改编上述代码的完整示例

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

// definition of a comparator interface needed by the sort function 
// to compare the values in the array passed as 'void *'
typedef int (*comparator)(void *, void *);

// implementation of the comparator interface for the int type
int int_comparator(void *a, void *b)
{
    int *aa = a;
    int *bb = b;
    return (*aa > *bb) - (*aa < *bb);
}

// generic swap, lacking error checking for the malloc call to keep things brief
void swap(void *a, void *b, size_t size)
{
    unsigned char *aa = a;
    unsigned char *bb = b;
    unsigned char *tmp = malloc(size);

    memcpy(tmp, aa, size);
    memcpy(aa, bb, size);
    memcpy(bb, tmp, size);
    free(tmp);
}

// takes the array, its length, the size of the type it contains, and a pointer 
// to a comparator function according to the type contained in the array
void shaker_sort(void *array, size_t length, size_t size, comparator cmp)
{
    // can't dereference a 'void *', so the array is 
    // now considered as a sequence of raw bytes
    unsigned char *arr = array;
    size_t start = 0;
    size_t end = length - 1;
    int swapped = 1;

    while (swapped) {
        swapped = 0;

        for (size_t i = start; i < end; i++) {
            // since we have a sequence of bytes, access to the original 
            // array elements happens by reading chunks of data of the
            // size of the type contained in the array
            if (cmp(&arr[i * size], &arr[i * size + size]) > 0) {
                swap(&arr[i * size], &arr[i * size + size], size);
                swapped = 1;
            }
        }

        if (!swapped) break;

        swapped = 0;
        end--;

        for (size_t i = end; i >= start; i--) {
            if (cmp(&arr[i * size], &arr[i * size + size]) > 0) {
                swap(&arr[i * size], &arr[i * size + size], size);
                swapped = 1;
            }
        }

        start++;
    }
}

int main(void)
{
    int arr[] = {3, 0, -4, 6, 1};
    size_t length = sizeof(arr) / sizeof(int);

    shaker_sort(arr, length, sizeof(int), int_comparator);

    for (size_t i = 0; i < length; i++) {
        printf("%d ", arr[i]);
    }

    puts("");
}

Compiling with gcc -Wall -Wextra -pedantic -std=c11 test.c -o test is fine, but then it goes into segmentation fault.使用gcc -Wall -Wextra -pedantic -std=c11 test.c -o test编译很好,但随后会进入分段错误。 A quick run of valgrind --tool=memcheck --leak-check=full ./test shows that apparently I'm using uninitialized values, performing invalid reads, and other amenities.快速运行valgrind --tool=memcheck --leak-check=full ./test表明显然我正在使用未初始化的值、执行无效读取和其他便利。 For the sake of brevity I'm not including the output but you can just copy the whole code and reproduce my exact results.为简洁起见,我不包括输出,但您可以复制整个代码并重现我的确切结果。

Now, the weird thing is that the code works perfectly with a clean valgrind output if I write the second for loop of the Shaker Sort like this:现在,奇怪的是,如果我像这样编写 Shaker Sort 的第二个 for 循环,代码可以与干净的 valgrind 输出完美配合:

for (size_t i = end; i > start; i--) {
    if (cmp(&arr[i * size], &arr[i * size - size]) < 0) {
        swap(&arr[i * size], &arr[i * size - size], size);
        swapped = 1;
    }
}

Basically the loop now stops at the element in position start + 1 and, instead of comparing the current element with its successor like before, it compares the current one with its predecessor .基本上循环现在在位置start + 1的元素处停止,而不是像以前一样将当前元素与其后继元素进行比较,而是将当前元素与其前驱元素进行比较 And that's it, I haven't got the slightest idea why the code in its pristine form is fine in C# and possibly Java and other languages, but in C it requires this small adjustment.就是这样,我完全不知道为什么原始形式的代码在 C# 中可能还可以在 Java 和其他语言中使用,但在 C 中它需要这个小的调整。 Can somebody shed some light on the matter?有人可以对此事有所了解吗?

start and i are unsigned, starti没有签名,

    for (size_t i = end; i >= start; i--)

first time here start is 0第一次这里start是 0

I counts down to 0 and then subtracting 1 from 0 gets you some other value that being unsigned is either greater-than or equal to zero and the loop continues我倒数到 0,然后从 0 中减去 1 得到一些其他值,即无符号大于或等于零,循环继续

do this instead:这样做:

    for (size_t i = end; i > start; i--) {
        if (cmp(&arr[i * size - size], &arr[i * size]) > 0) {
            swap(&arr[i * size - size ], &arr[i * size], size);
            swapped = 1;
        }

    }

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

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