c - Qsort使用的比较功能-比较(char **)

标签 c sorting

下面是比较函数:

int compare(const void *a, const void *b) {
    char* *s = (char* *) a;
    char* *t = (char* *) b;
    return sort_order * strcmp(*s, *t); // sort_order is -1 or 1
}

现在,我的问题是,选择一个特定类型的double pointer的原因是什么?或者更确切地说,为什么需要双指针转换,以及如何在内部使用它?
使用的其他变量:
char **wordlist;int nbr_words;(数组元素为)char *word;
出口呼叫:qsort(wordlist, nbr_words, sizeof(char *), compare);

最佳答案

如果您显示了wordlist的定义,这会有所帮助,但很可能它被定义为char **compare()函数接收指向列表中每个元素的指针。如果列表中的每个元素都是char *类型,那么compare()将接收到两个指向char *的指针,换句话说就是两个char **
转换为char **(注意,在这种特殊情况下,如果不从constvoid指针转换到非-constchar **)本身是多余的,因为qsort()必须处理任何类型,因此参数在传递之前都转换为void *。不能遵从avoid *,因此在对它们执行任何操作之前,必须将它们转换回原始类型。
例如:

#include <stdio.h>

int compare_int(void * a, void * b) {
    int * pa = a;
    int * pb = b;
    if ( *pa < *pb ) {
        return -1;
    } else if ( *pa > *pb ) {
        return 1;
    } else {
        return 0;
    }
}

int compare_double(void * a, void * b) {
    double * pa = a;
    double * pb = b;
    if ( *pa < *pb ) {
        return -1;
    } else if ( *pa > *pb ) {
        return 1;
    } else {
        return 0;
    }
}

int compare_any(void * a, void * b, int (*cfunc)(void *, void *)) {
    return cfunc(a, b);
}

int main(void) {
    int a = 1, b = 2;
    if ( compare_any(&a, &b, compare_int) ) {
        puts("a and b are not equal");
    } else {
        puts("a and b are equal");
    }

    double c = 3.0, d = 3.0;
    if ( compare_any(&c, &d, compare_double) ) {
        puts("c and d are not equal");
    } else {
        puts("c and d are equal");
    }

    return 0;
}

输出:
paul@local:~/src/c/scratch$ ./comp
a and b are not equal
c and d are equal
paul@local:~/src/c/scratch$

compare_any()函数将比较支持的任何类型,在本例中是intdouble,因为我们可以向它传递函数指针。但是,传递函数的签名必须相同,因此不能声明compare_int()接受两个int *参数,也不能声明compare_double()接受两个double *参数。我们必须声明它们都有两个void *参数,当我们这样做时,我们必须将这些void *参数转换为这些函数中的有用参数,然后才能使用它们。
在你的例子中发生的事情是完全相同的,但是数据本身是指针,所以我们将指针传递给指针,所以我们需要将void *转换成,在你的例子中,char **
编辑:为了解释对原来关于qsort()如何工作的问题的评论中的一些混淆,这里有qsort()签名:
void qsort(void *base, size_t nmemb, size_t size,
           int(*compar)(const void*, const void*))

base是指向数组第一个元素的指针,nmemb是该数组的成员数,size是每个元素的大小。
qsort()调用数组的第一个和第二个元素时,它将发送第一个元素的地址(即compar本身)和元素的地址(即base)。
如果base + size最初声明为base的数组,那么compare函数必须将接收到的指针解释为int,即int。如果int *最初声明为字符串数组,则compare函数必须将这些指针解释为指向base的指针,即char **
在所有情况下,compare函数都会获取指向元素的指针。如果有一个char *数组,那么必须在compare函数中将这些指针解释为char **。如果有int数组,则必须将其解释为int *,依此类推。
在这种情况下,如果只向compare函数传递了普通的char *参数,显然可以调用char **。但是,由于strcmp()是泛型的,它只能将指针传递给compare函数,实际上它不能传递元素的值-这是使用char *允许它是泛型的,因为任何类型的对象指针都可以转换为qsort(),但是没有可转换任何非指针值的等效数据类型。因此,它必须与常规类型(如void *void *)、指针和结构(structs)的工作方式相同,而让它正确处理所有可能类型的唯一方法是让它处理指向元素的指针,而不是元素本身,即使元素本身也是指针。出于这个原因,这里看起来您获得了一个不必要的间接级别,但实际上,为了使int能够以其通用的方式工作,这是必要的。
如果我修改上面的代码,使double更类似于qsort(),并且不接受两个指针,而是指向各种类型的两个元素数组的一个指针,您可以更清楚地看到这一点(稍微做作的示例,但我们将其保持简单):
#include <stdio.h>
#include <string.h>

int compare_int(void * a, void * b) {
    int * pa = a;
    int * pb = b;
    if ( *pa < *pb ) {
        return -1;
    } else if ( *pa > *pb ) {
        return 1;
    } else {
        return 0;
    }
}

int compare_double(void * a, void * b) {
    double * pa = a;
    double * pb = b;
    if ( *pa < *pb ) {
        return -1;
    } else if ( *pa > *pb ) {
        return 1;
    } else {
        return 0;
    }
}

int compare_string(void * a, void * b) {
    char ** pa = a;
    char ** pb = b;
    return strcmp(*pa, *pb);
}

int compare_any(void * arr, size_t size, int (*cfunc)(void *, void *)) {
    char * first = arr;
    char * second = first + size;
    return cfunc(first, second);
}

int main(void) {
    int n[2] = {1, 2};
    if ( compare_any(n, sizeof(*n), compare_int) ) {
        puts("a and b are not equal");
    } else {
        puts("a and b are equal");
    }

    double d[2] = {3.0, 3.0};
    if ( compare_any(d, sizeof(*d), compare_double) ) {
        puts("c and d are not equal");
    } else {
        puts("c and d are equal");
    }

    char * s[] = {"abcd", "bcde"};
    if ( compare_any(s, sizeof(*s), compare_string) ) {
        puts("'abcd' and 'bcde' are not equal");
    } else {
        puts("'abcd' and 'bcde' are equal");
    }

    return 0;
}

输出:
paul@local:~/src/c/scratch$ ./comp
a and b are not equal
c and d are equal
'abcd' and 'bcde' are not equal
paul@local:~/src/c/scratch$

如您所见,如果compare_any()函数没有得到它需要视为qsort()的指针,则compare_any()无法同时接受int数组和char *数组,因为它对数组元素执行指针运算。如果没有这种额外的间接作用,无论是compare_string()还是char **都不能起作用。

关于c - Qsort使用的比较功能-比较(char **),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19527504/

相关文章:

java - 如何根据 myobject 的属性对 ArrayList<myobject> 进行排序

c++ - 使用标准库对 vector 的 vector 进行排序并计算唯一出现的频率

python - 如何将数字更改为科学数字并获得最小值(科学数字格式)

java - 从最近到最远对 Java 列表进行排序(具有多个键)

Java,使用比较器对ArrayList进行排序

从不同大小的整数转换为指针?

c# - 将 pinvoke 与结构和指针一起使用

c - 在 C 中,即使从不同来源编译示例代码,strtok 也会导致我的程序崩溃

java - 可以将 Java 数组传递给采用数组的 C/C++ 函数吗?

c - 传递给 pthread_create 的值改变了