简体   繁体   English

如何使用本机 qsort 在 C 中对 `int **` 数组进行排序

[英]How to sort an `int **` array in C with native qsort

I've been unable to find any question regarding this, and I think I'm going a bit crazy trying to figure this out.我一直找不到关于此的任何问题,我想我想弄清楚这一点有点疯狂。

I have the following code:我有以下代码:

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

int cmp_int(const void *a, const void *b)
{
  return * (int *)a - * (int *)b;
}

int main(int argc, char *argv[])
{
  int n = 10;
  int **arr = calloc(n, sizeof(int *));
  srand((unsigned int) time(NULL));
  for (int i = n-1; i >= 0; i--) {
    arr[i] = calloc(1, sizeof(int));
    *(arr[i]) = rand() % 1000;
  }
  for (int i = 0; i < n; i++)
    printf("%d ", *(arr[i]));
  printf("\n");
  qsort(arr, 10, sizeof(void *), cmp_int);
  for (int i = 0; i < n; i++)
    printf("%d ", *(arr[i]));
  printf("\n");
  free(arr);
  return 0;
}

It's super basic, right?这是超级基本的,对吧? According to the manpage, the first argument is the pointer to the base element and the third argument is the size.根据联机帮助页,第一个参数是指向基本元素的指针,第三个参数是大小。 However, I fail to get the array as a sorted result.但是,我无法将数组作为排序结果。 I'm still really confused as to what the first and third argument to qsort should be since I suspect that that's where the fault is.对于 qsort 的第一个和第三个参数应该是什么,我仍然很困惑,因为我怀疑这就是问题所在。

Any help is appreciated.任何帮助表示赞赏。

Thanks.谢谢。

Edit: I should add that this code obviously does no error checking and that I was trying to test qsort with a double-pointer array of integers, so while yes I could use a regular array that was not the intended purpose of this code (it's actually part of a bigger segment in a separate program).编辑:我应该补充一点,这段代码显然没有进行错误检查,而且我试图用一个双指针整数数组来测试 qsort,所以虽然是的,我可以使用一个不是此代码预期目的的常规数组(它是实际上是单独程序中更大段的一部分)。

Your program makes my head hurt.你的程序让我头疼。 The reason you're not getting a correct sort is that the comparison function is wrong.您没有得到正确排序的原因是比较函数是错误的。 It would need to be return **(int **)a - **(int **)b;它需要return **(int **)a - **(int **)b; to get a correct result.以获得正确的结果。

However it's not worth fixing the problem that way.但是,以这种方式解决问题是不值得的。 A list of at least some of the issues:至少列出一些问题的清单:

  • If you don't use argc and argv , don't declare them.如果您不使用argcargv ,请不要声明它们。
  • Cast in the srand call is unnecessary.srand调用中强制转换是不必要的。
  • int comparison by subtraction is a bad idea because it can overflow.通过减法进行int比较是一个坏主意,因为它可能会溢出。
  • calloc returns should always be checked for null (out of memory) results.应始终检查calloc返回的 null(内存不足)结果。
  • calloc isn't needed at all.根本不需要calloc Use a variable length array.使用可变长度数组。
  • There's no need to allocate an array of pointers to ints.无需分配指向 int 的指针数组。 Just allocate an array of ints.只需分配一个整数数组。 Then your comparison works as-is.然后您的比较按原样进行。
  • The qsort call uses a hard constant 10 rather than n . qsort调用使用硬常数 10 而不是n
  • It's less error prone to give the element size by dereferencing the array name.通过取消引用数组名称来给出元素大小不太容易出错。
  • At the end you free the "spine" array but never the integer elements.最后,您释放了“spine”数组,但永远不会释放整数元素。
  • You should factor out a function to print the array.您应该分解出一个函数来打印数组。

Here's a version that addresses these.这是一个解决这些问题的版本。

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

int cmp_int(const void *va, const void *vb)
{
  int a = *(int *)va, b = *(int *) vb;
  return a < b ? -1 : a > b ? +1 : 0;
}

void print(int *a, int n) {
  for (int i = 0; i < n; ++i) printf("%d ", a[i]);
  printf("\n");
}

int main(void)
{
  int n = 10, a[n];
  srand(time(0));
  for (int i = 0; i < n; ++i) a[i] = rand() % 1000;
  print(a, n);
  qsort(a, n, sizeof a[0], cmp_int);
  print(a, n);
  return 0;
}

The problem you are having is failing to account for one additional level of indirection created by allocating for a block of pointers with int **arr = calloc (n, sizeof *arr);您遇到的问题是无法解释通过使用int **arr = calloc (n, sizeof *arr);分配一个指针块而创建的一个额外的间接级别int **arr = calloc (n, sizeof *arr); and then allocating storage for a single int to each pointer with arr[i] = calloc (1, sizeof *arr[i]) .然后使用arr[i] = calloc (1, sizeof *arr[i])为每个指针分配单个int存储空间。

Since the int compare (const void *a, const void *b) compare function for qsort expects a pointer to the elements of the array being sorted, both a and b above will be pointer-to-pointer to int in your case requiring 2 levels of indirection be dereferenced before the integer values can be compared.由于qsortint compare (const void *a, const void *b)比较函数需要一个指向正在排序的数组元素的指针,因此上面的ab都是指向int的指针,需要 2在可以比较整数值之前取消引用间接级别。

Rather than cmp_int , you actually need a cmp_int_ptr compare function.而不是cmp_int ,您实际上需要一个cmp_int_ptr比较函数。 It can be written as:可以写成:

int cmp_int_ptr (const void *a, const void *b)
{
    int *ai = *(int * const *)a,
        *bi = *(int * const *)b;

    return (*ai > *bi) - (*ai < *bi);
}

( note: the two levels of indirection in the cast (int * const *) ... which can also be written as (int **) , but to correspond to the parameter type (const void *) the (int * const *) is proper) 注意: cast (int * const *) ... 中的两个间接级别也可以写成(int **) ,但要对应参数类型(const void *)(int * const *)是正确的)

Putting that in place, adding validations for each allocation and cleaning up your calloc type-size specification by using the dereferenced pointer itself to set type-size, you can do:把它放在适当的位置,为每个分配添加验证并通过使用取消引用的指针本身来设置类型大小来清理你的calloc类型大小规范,你可以这样做:

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

int cmp_int_ptr (const void *a, const void *b)
{
    int *ai = *(int * const *)a,
        *bi = *(int * const *)b;

    return (*ai > *bi) - (*ai < *bi);
}

int main (void) {

    int n = 10;
    int **arr = calloc (n, sizeof *arr);

    if (!arr) {
        perror ("calloc-arr");
        return 1;
    }

    srand((unsigned int) time(NULL));

    for (int i = 0; i < n; i++) {
        if (!(arr[i] = calloc (1, sizeof *arr[i]))) {
            perror ("calloc-arr[i]");
            return 1;
        }
        *(arr[i]) = rand() % 1000;
    }

    for (int i = 0; i < n; i++)
        printf (" %d", *(arr[i]));
    putchar ('\n');

    qsort (arr, 10, sizeof *arr, cmp_int_ptr);

    for (int i = 0; i < n; i++) {
        printf (" %d", *(arr[i]));
        free (arr[i]);              /* don't forget to free your int allocated */
    }
    putchar ('\n');

    free(arr);                      /* now free pointers */
}

Example Use/Output示例使用/输出

$ ./bin/qsortptrtoint
 654 99 402 264 680 534 155 533 397 678
 99 155 264 397 402 533 534 654 678 680

Look things over and let me know if you have questions.仔细检查一下,如果您有任何问题,请告诉我。

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

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