此C qsort函数调用中的参数是什么?

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; } 

(int (*)(const void *,const void *))表示“将后面的内容视为指向函数的指针,该函数接受const void*类型的两个参数并返回一个int ”。 compare_wid确实是一个可以这种方式处理的函数。

qsort将调用此函数在排序时执行项之间的比较:如果返回的整数为零,则假定项相等,否则使用整数的符号对它们进行排序。

我将稍微了解一下该行的含义,但在我这样做之前,让我们先了解为什么qsort()需要它所需类型的最终参数的一些基础知识。 qsort()是一个可以对任何类型的数据进行排序的函数。

你提供:

  • 一个基指针,指向一个连续数据块的开头,
  • 块中的元素数量,
  • 一个数据成员的大小,和
  • 一个比较两个数据值的函数。

由于排序算法通常不依赖于正在排序的数据的类型,因此可以在不知道它正在排序的数据类型的情况下编写qsort() 。 但是为了能够做到这一点, qsort()接受void *参数,这意味着C中的“generics指针”。

假设您有一组未排序的int值:

 #define N 1024 int data[N] = { 10, 2, 3, -1, ... } /* 1024 values */ 

然后你可以通过调用qsort()来对它们进行排序:

 qsort(data, N, sizeof data[0], compare_int); 

传递给qsort()data的类型为int *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()所需的类型。

但是,这有问题。 类型的function:

 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  #include  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指针与结构指针的类型不同,并且在某些机器上它可能具有不同的大小或表示(这就是为什么这些强制转换是正确性的原因)。

如常见问题解答中所述,也可以看到这一点 。

快速排序

 void qsort ( void * base, size_t num, size_t size, int ( * comparator ) ( const void *, const void * ) ); 

比较器 :比较两个元素的函数。 该function应遵循以下原型:

int comparator(const void * elem1,const void * elem2);

该函数必须接受两个指向元素的参数,类型为void *。 应将这些参数转换回某些数据类型并进行比较。

该函数的返回值应表示elem1是否被认为小于,等于或大于elem2,分别返回负值,零或正值。

第四个参数包含对函数指针的显式强制转换:

 int (*)(const void *,const void *) 
  • int部分对应于返回类型
  • (*)部分是函数指针的表示法
  • (const void *,const void *)部分是参数类型

它是一个谓词函数,由qsort实现调用以比较两个TRELLIS_ATOM*

这是TRELLIS_ATOM类型元素的自定义排序函数(无论可能是什么)。

如果*a < *bcompare_wid应该返回<0,如果*a < *b ,则返回> 0,如果*a*b在逻辑上被视为相等,则返回0。

这是一个quicksort函数,它需要一个指向自定义排序处理程序例程的函数指针。 因此,对第四个参数中的函数指针的参数进行类型转换。

当调用快速排序函数时,它会执行您提供的函数指针来处理排序算法,即TRELLIS_ATOM *aTRELLIS_ATOM *b ,然后在两者之间进行比较检查,并返回1,0,或逻辑比较中的-1,例如:

 if(* a> * b)返回1;
 if(* a == * b)返回0;
 if(* a <* b)返回-1;

希望这会有所帮助,最好的问候,汤姆。

 /* 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 ); }