我是C的初学者,我正在尝试理解qsort函数所需的比较函数。
一个简单的建议用法是这个(我已经包含了一些main()代码来打印结果):
#include <stdio.h>
#include <stdlib.h>
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 a-b;
}
出于某种原因,使用多维数组,我能够逃避不使用指针,并且由于某种原因它起作用了!到底是怎么回事? (通过每个子数组中的第二项排序多维数组的示例代码):
#include <stdio.h>
#include <stdlib.h>
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;
}
我很高兴多维数组排序正在工作,因为这是我的最终目标,但我不知道我是如何设法让它工作的(除了愚蠢的运气和切碎代码)所以我真的很想解释为什么我提供的一些例子工作,以及为什么有些不工作!
答案 0 :(得分:12)
这仍然有效,并产生相同的结果。任何人都可以向我解释我删除了什么,为什么它仍然有效?
您正在C中调用未定义的行为。请参阅C99 6.3.2.3指针/ 8:
指向一种类型的函数的指针可以转换为指向另一种函数的指针 打字再回来;结果应该等于原始指针。如果转换了 指针用于调用类型与指向类型不兼容的函数, 行为未定义。
在C ++中,这个程序是不完整的:http://ideone.com/9zRYSj
它仍然“正常工作”,因为compare
函数需要一对指针;并且在您的特定平台上sizeof(void*)
与sizeof(int*)
相同,因此调用类型为int(void *, void *)
的函数指针实际上包含指向类型int(int *, int *)
的函数的指针< em>有效与在特定时间点在特定平台上投射的指针类型相同。
另外,我真的需要使用指针吗?为什么我不能直接比较“a”和“b”(这不起作用):
因为qsort
对任何两种类型都采用一般比较函数;不只是int
。因此它不知道指针被解除引用的类型。
出于某种原因,使用多维数组,我能够逃避不使用指针,并且由于某种原因它起作用了!发生了什么!
这是因为以下原型是相同的:
int foo(int *a, int *b);
int foo(int a[], int b[])
也就是说,当传递给函数时,数组将衰减成指针。像你一样明确指定数组的长度:
int foo(int a[2], int b[2])
使编译器使sizeof
和其他编译时间位将项目视为两元素数组;但是当它下降到机器级别时,该函数仍然接受一对指针。
在任何这些情况下,传递不带一对void *
的比较函数会导致未定义的行为。 “未定义行为”的一个有效结果是“它似乎工作”。另一个有效的结果是“它适用于星期二”或“它格式化硬盘”。不要依赖这种行为。
答案 1 :(得分:2)
我不明白为什么你需要比较中的所有额外的东西 功能所以我把它简化为这个
您是否使用const
限定符取决于您。您不应修改比较器中的值。但是有可能抛弃const
并打破你对编译器的承诺。
qsort 需要一个函数指针,它将两个const void *
作为参数,这就是为比较器函数传递指针的原因:
void qsort(void *base, size_t nmemb, size_t size,
int(*compare)(const void *, const void *));
因此传递a
和b
会导致将值解释为指针,这显然是错误的。
它可以在不传递多维数组的指针的情况下工作,因为当你传递数组时,它们会衰减为指针。所以您可以使用以下比较器。
int compare (int a[2], int b[2])
{
return a[1] - b[1];
}
答案 2 :(得分:0)
答案很简单:从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
所以你不应该使用不同的原型。
答案 3 :(得分:0)
我想确认Dlinet提出原始问题的观察结果。
C编译器确实会接受比较函数的所有形式,即使没有const,甚至使用int *而不是void *。但是,我的C ++编译器拒绝变量,只接受const void *形式。
C编译器甚至接受不使用指针的int形式。我检查并发现比较函数的int形式是接收指针值,而不是数组的int。结果是数组没有排序,但保持相同的顺序。而且我认为这一切都是你所期望的。
因此,似乎你可以在函数定义中使用int *而不是void *,然后不需要在函数内强制转换。
这是否是良好的编程习惯是没有实际意义的,一些程序员说是,有些说不。对我来说,良好的编程与否,减少杂乱将有利于使用int *而不是void *。但是,C ++编译器不允许你这样做。