【问题标题】:C sort 2D integer array with qsortC 使用 qsort 对二维整数数组进行排序
【发布时间】:2023-12-20 12:49:01
【问题描述】:

你有什么问题?

我正在尝试使用 C 中的 qsort 函数对二维整数数组进行排序,但我 出现分段错误。 当我使用以下命令编译它时:

gcc -g -lm -Werror -Wfatal-errors -Wall -Wextra -Wuninitialized -fsanitize=address -pedantic -Wshadow -std=c99 Test2.c

然后执行它,我收到以下错误消息:

AddressSanitizer:DEADLYSIGNAL
=================================================================
==347270==ERROR: AddressSanitizer: SEGV on unknown address 0x00147fff8c06 (pc 0x556f9e44d2bb bp 0x7ffe9684a5f0 sp 0x7ffe9684a5d0 T0)
==347270==The signal is caused by a READ memory access.
    #0 0x556f9e44d2bb in compare /windows/Programming/C/Random_Stuff/Test2.c:8
    #1 0x7f17aa2718d7 in msort_with_tmp.part.0 (/usr/lib/libc.so.6+0x3e8d7)
    #2 0x7f17aa2716b4 in msort_with_tmp.part.0 (/usr/lib/libc.so.6+0x3e6b4)
    #3 0x7f17aa2716b4 in msort_with_tmp.part.0 (/usr/lib/libc.so.6+0x3e6b4)
    #4 0x7f17aa271a79 in __qsort_r (/usr/lib/libc.so.6+0x3ea79)
    #5 0x556f9e44d657 in main /windows/Programming/C/Random_Stuff/Test2.c:64
    #6 0x7f17aa25ab24 in __libc_start_main (/usr/lib/libc.so.6+0x27b24)
    #7 0x556f9e44d13d in _start (/windows/Programming/C/Random_Stuff/a.out+0x113d)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /windows/Programming/C/Random_Stuff/Test2.c:8 in compare
==347270==ABORTING

提示:请不要被目录名称windows 激怒,我在 linux。

代码

简化示例

在以下代码中,我创建了一个 10x10 数组,如下所示:

[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9]
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
[30, 31, 32, 33, 34, 35, 36, 37, 38, 39]
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49]
[50, 51, 52, 53, 54, 55, 56, 57, 58, 59]
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69]
[70, 71, 72, 73, 74, 75, 76, 77, 78, 79]
[80, 81, 82, 83, 84, 85, 86, 87, 88, 89]
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]

现在我想根据每个子数组的特定索引以“第一个”数组的相反顺序对这些“行”进行排序。 所以它最终应该是这样的:

[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
[80, 81, 82, 83, 84, 85, 86, 87, 88, 89]
[70, 71, 72, 73, 74, 75, 76, 77, 78, 79]
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69]
[50, 51, 52, 53, 54, 55, 56, 57, 58, 59]
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49]
[30, 31, 32, 33, 34, 35, 36, 37, 38, 39]
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9]

原案

在我原来的情况下,我有类似的东西:

[8,  18, 2, 20, 0]
[13, 18, 3, 15, 1]
[8,  18, 3, 30, 2]
[8,  13, 3, 15, 3]
[8,  13, 2, 10, 4]
[8,  15, 1, 7, 5]
[13, 18, 2, 10, 6]
[13, 17, 6, 24, 7]
[8,  13, 1, 5, 8]
[8,  13, 2, 10, 9]
[13, 18, 2, 10, 10]
[8,  13, 4, 20, 11]
[8,  12, 4, 16, 12]
[8,  13, 1, 5, 13]
[13, 15, 8, 16, 14]
[9,  14, 6, 30, 15]
[8,  14, 1, 6, 16]

现在我想根据每个“子数组”的第四个值对这些“子数组”进行排序 子数组,所以它应该是这样的:

[8,  13, 1, 5, 8]
[8,  13, 1, 5, 13]
[8,  14, 1, 6, 16]
[8,  15, 1, 7, 5]
[8,  13, 2, 10, 4]
[13, 18, 2, 10, 6]
[8,  13, 2, 10, 9]
[13, 18, 2, 10, 10]
[13, 18, 3, 15, 1]
[8,  13, 3, 15, 3]
[8,  12, 4, 16, 12]
[13, 15, 8, 16, 14]
[8,  18, 2, 20, 0]
[8,  13, 4, 20, 11]
[13, 17, 6, 24, 7]
[8,  18, 3, 30, 2]
[9,  14, 6, 30, 15]

你有什么尝试?

我看到了这个帖子: Sorting a 2D array with qsort 但如果我将它与 发帖。

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

int compare(const void * a, const void * b)
{
    // "const void * a" points to "&nums[x]", right?
    unsigned short val1 = (*(unsigned short **) a) [3];
    unsigned short val2 = (*(unsigned short **) b) [3];

    if (val1 < val2)
        return 1;

    else if (val1 == val2)
        return 0;

    return -1;
}

int main()
{
    unsigned short **    nums;
    unsigned short       num;
    unsigned short       index;
    unsigned short       index2;
    const unsigned short length     = 10;
    const unsigned short sub_length = 10;

    /* -----------------
     * Preparations
     * ----------------- */
    nums = malloc(sizeof(unsigned short *) * length);
    num  = 0;

    if (nums == NULL)
        return EXIT_FAILURE;

    /* ------------
     * Filling
     * ------------ */
    for (index = 0; index < length; index++)
    {
        nums [index] = malloc(sizeof(unsigned short) * sub_length);

        if (nums [index] == NULL)
            return EXIT_FAILURE;

        // Fill each subarray with random nums
        for (index2 = 0; index2 < sub_length; index2++)
            nums [index][index2] = num++;
    }

    // "Before" output
    printf("Before:\n");
    for (index = 0; index < length; index++)
    {
        printf("[");
        for (index2 = 0; index2 < sub_length - 1; index2++)
            printf("%2hu, ", nums [index][index2]);
        printf("%2hu]\n", nums [index][sub_length - 1]);
    }

    // ================
    // What am I doing wrong?
    qsort(nums, length, sizeof(unsigned short) * sub_length, compare);
    // ================

    // "After" output
    printf("After:\n");
    for (index = 0; index < length; index++)
    {
        printf("[");
        for (index2 = 0; index2 < sub_length - 1; index2++)
            printf("%2hu, ", nums [index][index2]);
        printf("%2hu]\n", nums [index][sub_length - 1]);
    }

    /* ------------
     * Cleanup
     * ------------ */
    for (index = 0; index < length; index++)
        free(nums [index]);
    free(nums);

    return EXIT_SUCCESS;
}

【问题讨论】:

  • 不清楚您要对什么进行排序。你想对数组的每一“行”进行排序还是按行对数组进行排序?
  • 我想以相反的顺序对数组的每一“行”进行排序。我会编辑我的帖子,谢谢你的提示!
  • 你根本没有数组。你有一个 pointer-to-pointer-to unsigned short.
  • 阅读this article 了解调试代码的技巧。

标签: arrays c multidimensional-array qsort


【解决方案1】:

为了对行进行排序,您必须了解qsort 的相邻元素将是指向 unsigned short 的指针。因为qsort compare 需要一个指向相邻元素的指针,这将是一个指向指向unsigned short 的指针。因此,您在比较中需要处理两级间接,例如

/* compare for adjacent ROW pointers - descending order */
int compare_desc (const void *a, const void *b)
{
    unsigned short *pa = *(unsigned short * const *)a,
                   *pb = *(unsigned short * const *)b;
    
    return (*pa < *pb) - (*pa > *pb);
}

现在一个简短的例子是:

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

#define ROWS 10
#define COLS ROWS

/* compare for adjacent ROW pointers - descending order */
int compare_desc (const void *a, const void *b)
{
    unsigned short *pa = *(unsigned short * const *)a,
                   *pb = *(unsigned short * const *)b;
    
    return (*pa < *pb) - (*pa > *pb);
}

int main (void) {
    
    unsigned short **nums = NULL;
    
    nums = malloc (ROWS * sizeof *nums);
    if (!nums) {
        perror ("malloc-nums");
        exit (EXIT_FAILURE);
    }
    
    for (int i = 0; i < ROWS; i++) {
        nums[i] = malloc (COLS * sizeof *nums[i]);
        if (!nums[i]) {
            perror ("malloc-nums[i]");
            exit (EXIT_FAILURE);
        }
        for (int j = 0; j < COLS; j++) {
            nums[i][j] = i * COLS + j;
            printf (j ? " %3hu" : "%3hu", nums[i][j]);
        }
        putchar ('\n');
    }
    
    qsort (nums, ROWS, sizeof *nums, compare_desc);
    
    puts ("\nrows sorted descending, columns sorted ascending\n");
    for (int i = 0; i < ROWS; i++) {
        for (int j = 0; j < COLS; j++)
            printf (j ? " %3hu" : "%3hu", nums[i][j]);
        putchar ('\n');
        free (nums[i]);
    }
    free (nums);
}

使用/输出示例

$ ./bin/qsort_ptr2ptr
  0   1   2   3   4   5   6   7   8   9
 10  11  12  13  14  15  16  17  18  19
 20  21  22  23  24  25  26  27  28  29
 30  31  32  33  34  35  36  37  38  39
 40  41  42  43  44  45  46  47  48  49
 50  51  52  53  54  55  56  57  58  59
 60  61  62  63  64  65  66  67  68  69
 70  71  72  73  74  75  76  77  78  79
 80  81  82  83  84  85  86  87  88  89
 90  91  92  93  94  95  96  97  98  99

rows sorted descending, columns sorted ascending

 90  91  92  93  94  95  96  97  98  99
 80  81  82  83  84  85  86  87  88  89
 70  71  72  73  74  75  76  77  78  79
 60  61  62  63  64  65  66  67  68  69
 50  51  52  53  54  55  56  57  58  59
 40  41  42  43  44  45  46  47  48  49
 30  31  32  33  34  35  36  37  38  39
 20  21  22  23  24  25  26  27  28  29
 10  11  12  13  14  15  16  17  18  19
  0   1   2   3   4   5   6   7   8   9

如果这就是你所追求的,如果你还有其他问题,请告诉我。

【讨论】:

  • 是的!谢谢你。这正是我想要的!
  • 如果您总是回答“我要排序的元素是什么?”这个问题,并且您知道比较函数将采用指向该类型的指针——使用@987654328 永远不会有任何问题@。在这里,您的元素是指向 unsigned short(例如unsigned short*)的指针。因此比较参数是unsigned short **,除非你看,比较函数接受一个常量指针,所以类型实际上是unsigned short * const*(但unsigned short** 可以工作,你只会被警告丢弃const 限定符将启用完整警告:)
  • 感谢您的解释:)!我有一个(小)问题:如果你不能称这个二维“东西”为“数组”,我该如何称呼它呢? “指向整数列表的指针列表”还是有一个短名称?另外我只是“允许”所以说“数组”,如果类型有这种形式int[],对吧?
  • 数组是 C 中的一种特定类型。所有元素(字节)都保证在内存中是连续的。在type **nums; 的情况下,你有一个单指针(指向什么?),一个指针块,所以你有一个 指向指针的指针 type。您一次分配所有指针,它们将在内存中连续。这就是为什么你可以做num[i],但是你要分别分配持有unsigned short 的内存块。因此,一个 10 块 unsigned short 的地址与另一个块之间没有任何关联。所以没有数组,只有一块指针和10块unsigned short
最近更新 更多