[英]What are the parameters in this C qsort function call?
qsort(bt->rw[t], bt->num[t],
sizeof(TRELLIS_ATOM *),
(int (*)(const void *,const void *))compare_wid);
bt->rw[t]
是一个指向struct指针的指针, bt->[num]
是一个int
,我不明白第四个参数是什么,除了compare_wid是在某处定义的函数,如下所示:
static int compare_wid( TRELLIS_ATOM* a, TRELLIS_ATOM* b )
{
...
return x;
}
我将稍微了解该行的含义,但在我这样做之前,让我们了解为什么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()
并且所述第一参数qsort()
的类型为void *
。 由于任何对象指针都可以在C中转换为void *
,这没关系。 接下来的两个论点也没问题。 最后一个参数compare_int
应该是一个带有两个const void *
参数并返回一个int
const void *
。 该函数将由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()
使用qsort()
,你的程序不仅更容易阅读,而且更正。
如果你不能改变compare_wid()
,那么写另一个函数:
static int compare_stub(const void *a, const void *b)
{
return compare_wid(a, b);
}
并使用compare_stub()
(不使用compare_stub()
)而不是compare_wid()
调用qsort()
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
as-it-is的合法性似乎存在一些混淆。 comp.lang.c FAQ,问题13.9有一个很好的解释(强调我的):
要理解为什么
qsort
比较函数中的好奇指针转换是必要的( 以及为什么在调用qsort
时函数指针的qsort
无法帮助 ),考虑qsort
如何工作是有用的。qsort
对被排序的数据的类型或表示一无所知:它只是在一小块内存中乱窜。 (所有它知道的块是它们的大小,你在qsort
的第三个参数中指定。)为了确定两个块是否需要交换,qsort
调用你的比较函数。 (要交换它们,它使用相当于memcpy
。)由于
qsort
以通用方式处理未知类型的内存块,因此它使用通用指针(void *)
来引用它们。 当qsort
调用您的比较函数时,它将作为参数传递给要比较的块的两个通用指针。 由于它传递通用指针,因此比较函数必须接受通用指针,并在操作指针之前将指针转换回适当的类型(即在执行比较之前)。void
指针与结构指针的类型不同,并且在某些机器上它可能具有不同的大小或表示(这就是为什么这些强制转换是正确性的原因)。
如常见问题解答中所述,也可以看到这一点 。
(int (*)(const void *,const void *))
表示“将后面的内容视为指向函数的指针,该函数接受类型为const void*
两个参数并返回一个int
”。 compare_wid
确实是一个可以这种方式处理的函数。
qsort
将调用此函数在排序时执行项目之间的比较:如果返回的整数为零,则假定项目相等,否则使用整数的符号对它们进行排序。
void qsort ( void * base, size_t num, size_t size,
int ( * comparator ) ( const void *, const void * ) );
比较器 :比较两个元素的函数。 该功能应遵循以下原型:
int comparator(const void * elem1,const void * elem2);
该函数必须接受两个指向元素的参数,类型为void *。 应将这些参数转换回某些数据类型并进行比较。
该函数的返回值应表示elem1是否被认为小于,等于或大于elem2,分别返回负值,零或正值。
第四个参数包含对函数指针的显式强制转换:
int (*)(const void *,const void *)
int
部分对应于返回类型 (*)
部分是函数指针的表示法 (const void *,const void *)
部分是参数类型 /* Integer comparison function for use with the stdlib's Qsort. */
inline int int_compare(const void *p1, const void *p2) {
return ( *(int*)p1 - *(int*)p2 );
}
它是一个谓词函数,由qsort
实现调用以比较两个TRELLIS_ATOM*
。
这是TRELLIS_ATOM
类型元素的自定义排序函数(无论可能是什么)。
compare_wid
应返回的东西<0,如果*a < *b
,> 0,如果*a > *b
如果和0 *a
和*b
在逻辑上视为相等。
这是一个quicksort函数,它需要一个指向自定义排序处理程序例程的函数指针。 因此,对第四个参数中的函数指针的参数进行类型转换。
调用快速排序函数时,它会执行您提供的函数指针来处理排序算法,即TRELLIS_ATOM *a
和TRELLIS_ATOM *b
,然后在两者之间进行比较检查,并返回1,0,或逻辑比较中的-1,例如:
if (*a > *b) return 1; if (*a == *b) return 0; if (*a < *b) return -1;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.