比较Qsort使用的函数 - 比较(char **)

时间:2013-10-22 20:18:10

标签: 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;

Ex qsort call:qsort(wordlist, nbr_words, sizeof(char *), compare);

1 个答案:

答案 0 :(得分:6)

如果您显示wordlist的定义会有所帮助,但很可能将其定义为char **compare()函数接收指向列表中每个元素的指针。如果列表中的每个元素都是char *类型,那么compare()将会收到两个指向char *或两个char **的指针。

转换为char **(请注意,如果您没有从const无效指针进行操作,那么实际的强制转换将是多余的,在这种特殊情况下,对于非const char **)本身是必要的,因为qsort()必须处理任何类型的类型,因此参数在传递之前会转换为void *。您不能尊重void *,因此您必须先将它们转换回原始类型,然后再对其进行操作。

例如:

#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 *转换为case,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最初被声明为int的数组,那么compare函数必须将它接收的那些指针解释为int的指针,如int *。如果base最初被声明为字符串数组,作为char **,则compare函数必须将这些指针解释为指向char *的指针,即char **

在所有情况下,compare函数都会获得指向元素的指针。如果您有int数组,则必须在比较函数中将这些指针解释为int *。如果您有char *数组,则必须将其解释为char **,依此类推。

在这种情况下,如果您只是将普通strcmp()参数传递给compare函数,那么显然可以调用char *。但是,因为qsort()是通用的,它只能传递指向比较函数的指针,它实际上无法传递元素的值 - 它使用void *允许它是通用的,因为任何类型的对象指针都可以转换为void *,但没有等效的数据类型可以转换任何非指针值。出于这个原因,它必须以常规类型(如intdouble),指针和结构使用相同的方式,并且使其与所有可能类型一起正常工作的唯一方法是让它处理元素的指针,而不是元素本身,即使元素本身也是指针。出于这个原因,它可能似乎就像你在这里获得了不必要的间接级别一样,但实际上 是必要的,以便qsort()能够能够以通用的方式运作。

如果我修改上面的代码以便compare_any()更类似于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()无法接受int数组和char *数组,而没有compare_string()函数得到一个需要将其视为char **的指针,因为它对数组元素执行了指针运算。如果没有这种额外的间接水平,compare_int()compare_double()都无法发挥作用。