我会稍微了解该行的含义,但在此之前,我们来了解一下为什么qsort()
需要其所需类型的最终参数。 qsort()
是一种可以对任何类型的数据进行排序的函数。
你给它提供:
- 一个基指针,它指向数据的连续块的开始,
- 在块元素的数目,
- 一个数据的大小成员,并且
- 一个比较两个数据值的函数。
由于排序算法通常不依赖于排序数据的类型,因此可以在不知道排序数据类型的情况下编写qsort()
。但要能做到这一点,qsort()
需要void *
参数,这意味着“通用指针”中C.
比方说,你有无序int
值的数组:
#define N 1024
int data[N] = { 10, 2, 3, -1, ... } /* 1024 values */
然后你就可以对它们进行排序主叫qsort()
:
qsort(data, N, sizeof data[0], compare_int);
data
是int *
类型传递给qsort()
时,并且所述第一parame ter的qsort()
是void *
的类型。由于任何对象指针都可以在C中转换为void *
,所以这是可以的。接下来的两个参数也可以。最后一个参数compare_int
应该是一个函数,它需要两个const void *
参数并返回一个int
。该功能将由qsort()
调用,其中&data[0]
到&data[N-1]
的指针对数量根据需要多次调用。
将函数声明为f()
说:“有两个const void *
参数和返回int
”:
int f(const void *, const void *);
如果想声明一个函数指针,我们可以设置为f
,指针被声明为:
int (*pf)(const void *, const void *);
pf = f;
括号是必需的,否则pf
将返回int *
函数。现在,pf
是指向返回int
的函数的指针。
回到我们int
排序算法,并从上面,我们可以定义compare_int()
为:
int compare_int(const void *a, const void *b)
{
const int *the_a = a;
const int *the_b = b;
if (*the_a > *the_b) return 1;
else if (*the_a < *the_b) return -1;
else return 0;
}
在写compare_int()
,我们知道,通过指针int *
伪装成void *
,因此我们将它们转换回到int *
,这是好的,然后我们比较这些数字。现在
,我们把关注的代码有问题:
static int compare_wid(TRELLIS_ATOM* a, TRELLIS_ATOM* b)
意味着compare_wid
是一个函数,它有两个TRELLIS_ATOM *
参数,并返回一个int
。正如我们刚才看到的最后一个参数qsort()
应该是一个函数的类型是:
int (*)(const void *, const void *)
即函数接受两个const void *
参数和返回int
。由于类型不匹配,程序员将compare_wid()
转换为qsort()
所需的类型。
但是,这有问题。类型的函数:
int (*)(TRELLIS_ATOM *, TRELLIS_ATOM *)
不等同于类型的函数:
int (*)(const void *, const void *)
,所以它不能保证如果转换会工作。这是更容易,正确,规范申报compare_wid()
为:
static int compare_wid(const void *a, const void *b);
和compare_wid()
定义应该是这样的:
static int compare_wid(const void *a, const void *b)
{
const TRELLIS_ATOM *the_a = a;
const TRELLIS_ATOM *the_b = b;
...
/* Now do what you have to do to compare the_a and the_b */
return x;
}
如果你这样做,你将不再需要投在致电qsort()
,您的程序不仅会更容易阅读,而且也是正确的。
如果你不能改变compare_wid()
,然后写另一个功能:
static int compare_stub(const void *a, const void *b)
{
return compare_wid(a, b);
}
,并呼吁qsort()
与compare_stub()
(不投),而不是compare_wid()
。
编辑:基于许多错误的答案,这里是一个测试程序:
$ cat qs.c
#include <stdio.h>
#include <stdlib.h>
struct one_int {
int num;
};
#ifdef WRONG
static int compare(const struct one_int *a, const struct one_int *b)
{
#else
static int compare(const void *a_, const void *b_)
{
const struct one_int *a = a_;
const struct one_int *b = b_;
#endif
if (a->num > b->num) return 1;
else if (a->num < b->num) return -1;
else return 0;
}
int main(void)
{
struct one_int data[] = {
{ 42 },
{ 1 },
{ 100 }
};
size_t n = sizeof data/sizeof data[0];
qsort(data, n, sizeof data[0], compare);
return 0;
}
与compare()
编译定义为采用两个const struct one_int *
值:
$ gcc -DWRONG -ansi -pedantic -W -Wall qs.c
qs.c: In function `main':
qs.c:32: warning: passing argument 4 of `qsort' from incompatible pointer type
用正确的编译定义:
$ gcc -ansi -pedantic -W -Wall qs.c
$
编辑2:对qsort()
的最终参数使用compare_wid
的合法性似乎存在一些混淆。该comp.lang.c FAQ, question 13.9有一个很好的解释(重点煤矿):
要理解为什么在qsort
比较函数的指针好奇的转换是必要的(为什么叫qsort
当一个函数指针的投不禁) ,考虑qsort
的工作原理很有用。 qsort
不知道被排序数据的类型或表示形式:它只是在小块的内存之间进行混洗。 (所有关于块的知识都是它们的大小,您在qsort
的第三个参数中指定了它们的大小。)要确定两个块是否需要交换,qsort
将调用您的比较函数。 (交换它们,它使用的memcpy
等价物。)
由于qsort
优惠与未知类型的内存块一个通用的方法,它使用通用指针(void *)
引用它们。当qsort
调用你的比较函数时,它将两个通用指针作为参数传递给要比较的块。由于它传递了泛型指针,所以你的比较函数必须接受泛型指针,并且在操作它们之前(即在执行比较之前)将指针转换回适当的类型。 A void
指针与结构指针的类型不同,在某些机器上它可能具有不同的大小或表示形式(这就是为什么这些类型是正确要求的)。
正如常见问题解答中所述,另请参阅this。
写入的代码是错误的。详情请看我的答案。 – 2010-02-09 12:02:16
请参阅接受的问题的答案http://stackoverflow.com/questions/188839/function-pointer-cast-to-different-signature – 2010-02-09 12:10:23
@Alok:所写的代码是非常正确的。没有演员,这将是不正确的。 'compare_wid'衰变到一个指向返回一个整数并带两个指针的函数的指针。就调用代码而言,任何'T *'相当于'void *',并且'const'部分在类型检查之后消失。 – 2010-02-09 12:27:40