Qsort比较function

我是C的初学者,我正在尝试理解qsort函数所需的比较函数。

第一部分:语法

一个简单的建议用法就是这个(我已经包含了一些main()代码来打印结果):

#include  #include  int values[] = { 40, 10, 100, 90, 20, 25, 12, 13, 10, 40 }; int compare(const void *a, const void *b) { const int *ia = (const int *)a; // casting pointer types const int *ib = (const int *)b; return *ia - *ib; } int main() { int n; for (n=0; n<10; n++) { printf("%d ",values[n]); } printf("\n"); qsort(values, 10, sizeof(int), compare); for (n=0; n<10; n++) { printf("%d ",values[n]); } printf("\n"); system("pause"); return 0; } 

我不明白为什么你需要比较函数中的所有额外的东西,所以我简化为:

 int compare (int *a, int *b) { return *a-*b; } 

这仍然有效,并产生相同的结果。 任何人都可以向我解释我删除了什么,为什么它仍然有效?

第二部分:为什么指针?

另外,我真的需要使用指针吗? 为什么我不能直接比较“a”和“b”(这不起作用):

 int compare (int a, int b) { return ab; } 

出于某种原因,使用多维数组,我能够逃避不使用指针,并且由于某种原因它有效! 到底是怎么回事? (通过每个子数组中的第二项排序多维数组的示例代码):

 #include  #include  int values[7][3] = { {40,55}, {10,52}, {100,8}, {90,90}, {20,91}, {25,24} }; int compare(int a[2], int b[2]) { return a[1] - b[1]; } int main() { int n; for (n=0; n<6; n++) { printf("%d,",values[n][0]); printf("%d ",values[n][1]); } printf("\n"); qsort(values, 6, sizeof(int)*3, compare); for (n=0; n<6; n++) { printf("%d,",values[n][0]); printf("%d ",values[n][1]); } printf("\n"); system("pause"); return 0; } 

我很高兴多维数组排序工作,因为这是我的最终目标 ,但我不知道我是如何设法让它工作(除了愚蠢的运气和切断代码)所以我真的很喜欢一些解释为什么我提供的一些例子工作,为什么有些人不这样做!

这仍然有效,并产生相同的结果。 任何人都可以向我解释我删除了什么,为什么它仍然有效?

您正在C中调用未定义的行为。请参阅C99 6.3.2.3指针/ 8:

指向一种类型的函数的指针可以被转换为指向另一种类型的函数的指针并且再次返回; 结果应该等于原始指针。 如果转换的指针用于调用类型与指向类型不兼容的函数,则行为未定义。

在C ++中,这个程序是完全错误的: http : //ideone.com/9zRYSj

它仍然“正常工作”,因为compare函数需要一对指针; 并且在您的特定平台上, sizeof(void*)sizeof(int*) ,因此调用int(void *, void *)类型的函数指针实际上包含指向int(int *, int *)类型函数的指针int(int *, int *) 实际上与在特定时间点在特定平台上强制转换的指针类型相同。

另外,我真的需要使用指针吗? 为什么我不能直接比较“a”和“b”(这不起作用):

因为qsort对任何两种类型都采用一般比较函数; 不只是int 。 因此它不知道指针被解除引用的类型。

出于某种原因,使用多维数组,我能够逃避不使用指针,并且由于某种原因它有效! 到底是怎么回事!

这是因为以下原型是相同的:

  1. int foo(int *a, int *b);
  2. int foo(int a[], int b[])

也就是说,数组在传递给函数时会衰减成指针。 像你一样明确指定数组的长度:

 int foo(int a[2], int b[2]) 

导致编译器使sizeof和其他编译时间位将项目视为一个二元素数组; 但是当它下降到机器级别时,该函数仍然接受一对指针。

在任何这些情况下,传递不带一对void * s的比较函数会导致未定义的行为。 “未定义行为”的一个有效结果是“它似乎工作”。 另一个有效的结果是“它适用于星期二”或“它格式化硬盘”。 不要依赖这种行为。

我不明白为什么你需要比较函数中的所有额外的东西所以我简化它

是否使用const限定符取决于您。 因为,您不希望修改比较器中的值。 但是有可能抛弃const并打破你对编译器的承诺。


qsort需要一个函数指针,它将两个const void *作为参数,这就是为比较器函数传递指针的原因:

  void qsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, const void *)); 

所以传递ab会导致将解释为指针,这显然是错误的。


它可以在不传递多维数组指针的情况下工作,因为当你传递数组时,它们会衰减为指针。 所以你可以使用下面的比较器。

 int compare (int a[2], int b[2]) { return a[1] - b[1]; } 

答案很简单:从qsort http://pubs.opengroup.org/onlinepubs/009695399/functions/qsort.html的手册中,函数指针的原型是:

 int comp(const void *a, const void *b) 

与此原型关联的typedef可以这种方式声明

 typedef int (*comp_qsort_funct_t) ( const void *, const void * ) 

其中comp_qsort_funct_t是函数指针的类型

qsort的比较函数的原型是使用const变量,因为这是一个很好的实践/设计,因为数据不会被修改。

根据编译器/平台,使用不同的原型可能会导致意外结果。 或者只是无法编译,评论中提供了示例: http : //ideone.com/9zRYSj

所以你不应该使用不同的原型。

我想确认Dlinet提出原始问题的观察结果。

C编译器确实会接受比较函数的所有forms,即使没有const,甚至使用int *而不是void *。 但是,我的C ++编译器拒绝变量,只接受const void *forms。

C编译器甚至接受不使用指针的intforms。 我检查并发现比较函数的intforms是接收指针值,而不是数组的int。 结果是数组没有排序,但保持相同的顺序。 而且我认为这一切都是你所期望的。

因此,似乎您可以在函数定义中使用int *而不是void *,然后不需要在函数内转换。

这是否是一个好的编程实践是没有实际意义,一些程序员说是,有些说不。 对我来说,良好的编程与否,减少杂乱将有利于使用int *而不是void *。 但是,C ++编译器不会让你这样做。