【问题标题】:Segmentation fault on simple quick sort implementation简单快速排序实现的分段错误
【发布时间】:2021-09-05 14:05:48
【问题描述】:

所以我一直在尝试在 C 中创建一个通用的递归快速排序实现。为了使其通用,我使用了一个比较函数指针。此外,由于我们可以使用 Lomuto 或 Hoare 分区,我使用一个函数指针指向两个单独的函数,一个用于每个分区。现在我只完成了 Lomuto 部分。现在当然,比较函数不是用在快速排序函数本身上,而是用在分区函数上,所以我传递函数指针。应该很简单,但我迫切需要一些帮助,因为我在尝试调用该函数时不断遇到分段错误:

quicksort_recursive(array, 0, 8, sizeof(int), &cmpnum, &partition_lomuto);

这里是比较函数:

int cmpnum(const void* s1, const void* s2)
{
    int *a = (int*)s1;
    int *b = (int*)s2;
    if ((*a) > (*b))
        return 1;
    else if ((*a) < (*b))
        return -1;
    else
        return 0;
}

这是数组:

int array[] = [3, 7, 8, 5, 2, 1, 9, 5, 4];

肇事者在这里某处:

void swap(void *a, void *b, size_t size)
{
    char buffer[size];
    memcpy(buffer, a, size);
    memcpy(a, b, size);
    memcpy(b, buffer, size);
    return;
}

void *get(void *const array, size_t index, size_t size)
{
    return ((char *)array) + (index * size);
}

size_t partition_lomuto(void *const array, size_t low, size_t high, size_t size, __compar_fn_t compare)
{
    void *pivot = get(array, high, size);
    size_t i = low;
    for (size_t j = low; j <= high - 1; j++)
    {
        if(compare(get(array, j, size), pivot) <= 0)
        {
            i++;
            swap(get(array, i, size), get(array, j, size), size);
        }
    }
    swap(get(array, i + 1, size), get(array, high, size), size);
    return (i + 1);
}

void quicksort_recursive(void *const array, size_t low, size_t high, size_t size, __compar_fn_t compare, size_t (*partition)(void *const array, size_t low, size_t high, size_t size, __compar_fn_t compare))
{
    if (low < high)
    {
        size_t partition_index = partition(array, low, high, size, compare);
        quicksort_recursive(array, low, partition_index - 1, size, compare, partition);
        quicksort_recursive(array, low, partition_index + 1, size, compare, partition);
    }
    return;
}

【问题讨论】:

  • 触发 seg 错误的确切代码行是什么,您还进行了哪些其他调试?在调试器中运行您的程序。它会立即为您提供 seg fault 代码行,也可用于在程序运行时跟踪程序。 How to debug small programs
  • @kaylum 我对调试非常陌生,但我会尽力而为。我的调试方法是长时间关注代码并将 printfs 放在任何地方,但都没有产生任何有意义的结果。 CLion 的调试器说(我认为)分段错误是在交换函数的第 3 行(char buffer[size];)处触发的。
  • 我对调试很不熟悉”。如果是这种情况,那么最好先暂停编码并学习有效地使用调试工具。从长远来看,这将为您节省大量时间,并且比每次都在 SO 上发布以让其他人为您调试更有效率。
  • 即使对于最有经验的人来说,仅仅通过观察任何重要的代码来发现问题也很难甚至是不可能的。这就是为什么所有寻求调试帮助的 SO 问题都必须提供complete minimal reproducible example 以便任何人都可以运行和调试确切问题的主要原因之一。此外,暂停代码并不困难。您可以在 5 分钟内学会它。只需运行 break function_name 在您希望调试器停止的位置插入断点。
  • 打印语句调试是一种有效的方法,如果你将它应用到这段代码中,我想你会有所启发。

标签: c algorithm sorting segmentation-fault quicksort


【解决方案1】:

使用gcc -fsanitize=address ... 构建程序并运行它会导致以下错误:

=================================================================
==89==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff14f51124 at pc 0x7f766a029dd3 bp 0x7fff14f50f10 sp 0x7fff14f506c0
READ of size 4 at 0x7fff14f51124 thread T0
    #0 0x7f766a029dd2 in __interceptor_memcpy (/lib64/libasan.so.6+0x39dd2)
    #1 0x401381 in swap /tmp/t.c:19
    #2 0x401527 in partition_lomuto /tmp/t.c:44
    #3 0x401584 in quicksort_recursive /tmp/t.c:52
    #4 0x4018e0 in main /tmp/t.c:64
    #5 0x7f7669e4d1e1 in __libc_start_main (/lib64/libc.so.6+0x281e1)
    #6 0x4010dd in _start (/tmp/a.out+0x4010dd)

Address 0x7fff14f51124 is located in stack of thread T0 at offset 84 in frame
    #0 0x4015fc in main /tmp/t.c:60

  This frame has 1 object(s):
    [48, 84) 'array' (line 61) <== Memory access at offset 84 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow (/lib64/libasan.so.6+0x39dd2) in __interceptor_memcpy
Shadow bytes around the buggy address:
  0x1000629e21d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000629e21e0: 00 00 00 00 ca ca ca ca 04 cb cb cb cb cb cb cb
  0x1000629e21f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000629e2200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000629e2210: 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 f1 f1
=>0x1000629e2220: 00 00 00 00[04]f3 f3 f3 f3 f3 00 00 00 00 00 00
  0x1000629e2230: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000629e2240: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000629e2250: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000629e2260: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x1000629e2270: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==89==ABORTING

看起来错误就在这里:

    swap(get(array, i + 1, size), get(array, high, size), size);

虽然您知道i &lt;= highi + 1 可能会越界。

附:您有lowhigh 作为包含(封闭)间隔。如果使用半开区间的算法通常更容易推理。

【讨论】: