【问题标题】:Why this Shaker Sort code doesn't work in C为什么这个 Shaker Sort 代码在 C 中不起作用
【发布时间】:2020-03-29 21:35:15
【问题描述】:

我在 C 中实现了一个通用的 Shaker Sort 算法,各种网站以一种不断给我分段错误和其他错误的方式呈现代码,但在使用其他语言时它工作得很好。例如,this code 如果我将其保留在 C# 中,则没有问题,但在将其改编为 C 后它会停止工作。

这是我忠实改编上述代码的完整工作示例

#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("");
}

使用gcc -Wall -Wextra -pedantic -std=c11 test.c -o test 编译没问题,但随后会出现分段错误。快速运行valgrind --tool=memcheck --leak-check=full ./test 表明我正在使用未初始化的值、执行无效读取和其他便利。为简洁起见,我不包括输出,但您可以复制整个代码并重现我的确切结果。

现在,奇怪的是,如果我像这样编写 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;
    }
}

现在循环基本上停在位置start + 1 的元素处,而不是像以前那样将当前元素与其后继元素进行比较,它将当前元素与其前任元素进行比较。就是这样,我一点也不知道为什么原始形式的代码在 C# 和可能的 Java 和其他语言中是好的,但在 C 中它需要这个小的调整。有人能解释一下吗?

【问题讨论】:

  • main已经有了,需要向下滚动代码框。

标签: c sorting bubble-sort


【解决方案1】:

starti 未签名,

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

第一次来start是0

我倒数到 0,然后从 0 中减去 1,得到一些其他值,即无符号大于或等于 0,然后循环继续

改为这样做:

    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;
        }

    }

【讨论】:

  • 下溢对于无符号算术来说是完美定义的;但是,是的,这个下溢是问题所在。它将 i 变成一个仍然大于 0 的大正整数,因此主体会执行,但会查看一些不相关的内存。
  • 啊,肯定是下溢问题!我在网上找到的所有代码都假设变量是常规的ints,所以这就是它的行为方式。感谢您的帮助。
猜你喜欢
  • 2018-11-05
  • 1970-01-01
  • 1970-01-01
  • 2012-02-22
  • 2023-03-03
  • 2017-10-02
  • 2016-07-10
  • 2010-12-14
  • 2017-04-09
相关资源
最近更新 更多