qsort函数⽤法详解
qsort()函数⽤法详解
limabean
本⽂版权归作者所有,欢迎转载,但未经作者同意必须在⽂章页⾯给出原⽂链接,否则保留追究法律责任的权利。
1.qsort函数简介
排序是程序员经常碰到的问题,数据结构与算法的教科书上已经有许多标准的排序算法。在每次需要排序的时候,都要按照教科书重写⼀次代码?C语⾔早就考虑到了这个问题,已经提前编写好了⼀个快速排序的库函数,就是qsort。直接调⽤⼀下库函数,很简单对吗?试试看,哇,根本不知道怎么调⽤。楷书字体
qsort是C标准库<stdlib.h>库中的函数,使⽤时引⼊#include <stdlib.h>。
qsort函数的原型描述为:
void qsort( void *ba, size_t n_elements, size_t el_size,
int (*compar) (void const * , void const * ) )
这都是什么啊?解释⼀下,⼀共4个参数。第1个参数指向需要排序的数组,第2个参数指定数组中元素的数⽬,第3个参数指定每个元素的长度,以字节为单位,第4个参数是⼀个函数指针。
2.原理的第1个难点:回调函数
不能理解的就是第4个参数,这是什么东西,⼜怎么⽤呢?
qsort是提前编写好的C语⾔库函数,它不是神仙,不会提前知道⽤户在调⽤的时候是要对年龄、⾝⾼还是体重排序,不知道具体的数据类型,怎么写排序函数?
排序时,要执⾏两个元素⽐较⼤⼩的操作。只是⽐⼤⼩这个操作必须知道数据类型,如果把这个操作交给⽤户来做,就可以编写出与数据类型⽆关的排序函数。
⽤户调⽤库函数qsort,库函数qsort调⽤⽤户编写的⽐较函数,这样的⽤法称为回调函数。
问题是,库函数qsort要调⽤的⽤户函数,是将来调⽤它的⽤户编写的。库函数⽆法提前知道⽤户会给函数起个什么名,以及函数的参数列表,那库函数就⽆法调⽤⽤户函数。怎么处理呢?是在qsort中提前定义好⽤户函数的封装格式,参数列表以及函数的功能含义,不允许更改,⽤户只能编写功能代码,不能改变函数封装和功能含义。例如qsort的回调函数原型是:
int (*compar) (void const * , void const * )
⽤户函数返回值是int类型。(*compar)是⼀个函数指针,⽤函数指针替代具体的函数名,函数指针在使⽤前必须赋值,就是指向⼀个具体的函数。参数列表是中有两个参数,第1个参数,第2个参数。两个参数都是指针,指针的类型是void const,const的意思是不能修改指针所指向的内容,void是“⽆类型”的意思,⽆类型的指针是⽆法使⽤的,使⽤前必须进⾏强制类型转换,转换为指向某种数据类型的指针。
3.原理的第2个难点:⽐较函数
假设有这样⼀个问题:输⼊⼀组学⽣纪录,每条学⽣纪录由学号、姓名、成绩组成。输⼊⼀个标识变量C,当C=1时,按学号递增排序;当C=2时,按姓名的⾮递减字典序排序;当C=3时,按成绩的⾮递减排序。当若⼲学⽣具有相同姓名或者相同成绩时,则按他们的学号递增排序。现在,显然是要⽐较两个结构体变量的成员值。
先定义好结构体。
typedef struct Excle{
char id[7];
char name[9];
int score;
}Excle;
看⽤户的⽐较函数是如何写的。⽤户给函数起个名字,这个名字完全随⽤户⼼意。但⽤户不能改动函数的形参列表。
int comp1( const void a, const void b)
{
return strcmp( ((Excle)a).id , ((Excle)b).id );
}
在函数体内部,⽤户要完成两项⼯作:
⾸先,将第1个参数,第2个参数,这两个“⽆类型指针”强制转换为具体的数据类型指针。在此,就是将⽆类型指针a,b强制转换为指向结构体Excle的指针,(Excle*)a,⽆类型指针必须在强制转换为指向某⼀具体数据类型后,才能使⽤。
第⼆,⽐较两个元素的⼤⼩,并要保证⽐较的结果是如下表达。库函数qsort是这样理解⽤户函数功能的,⽤户不能更改函数的功能含义。
第1个参数 > 第2个参数 返回⼤于0的整数
第1个参数 = 第2个参数 返回整数0
第1个参数 < 第2个参数 返回⼩于0的整数
strcmp就是对两个字符串⽐较,结果就是上⾯的表达,所以直接返回其结果就可以了。排序时,当第⼀关键字相同时,需要按第⼆关键字排序,也是在此函数内完成。
⽤户如何调⽤qsort呢?
⾸先,⽤户要编写好⽐较函数,然后调⽤qsort。调⽤时,将⽤户编写的⽐较函数的名字作为参数,传给qsort。此处就是将comp1传给qsort。
商业摄影拍照
qsort(PX, n, sizeof(PX[0]), comp1);
注意不是在此处调⽤comp1,这⾥不是嵌套调⽤函数comp1,如果你理解为嵌套调⽤就⽆法理解了,因为此处没有传递函数参数。这⾥就是传递了个函数名字的字符串,在qsort内部,在合适的地⽅调⽤的是 (*compar) (a , b ),在调⽤前,comp1赋值给(*compar),a,b是两个待⽐较的元素的地址。
4.⼀个应⽤实例
问题:输⼊⼀组学⽣纪录,每条学⽣纪录由学号、姓名、成绩组成。输⼊⼀个标识变量C,当C=1时,按学号递增排序;当C=2时,按姓名的⾮递减字典序排序;当C=3时,按成绩的⾮递减排序。当若⼲学⽣具有相同姓名或者相同成绩时,则按他们的学号递增排序。
我们先写好3个⽐较函数,comp1是⽐较学号的⽐较函数,comp2⽐较姓名的⽐较函数,comp3是⽐较成绩的⽐较函数。
int comp1(const void a,const void b){
return strcmp( ((Excle)a).id,((Excle)b).id);
} ⽐较学号
int comp2(const void a,const void b){
if (strcmp(((Excle)a).name,((Excle)b).name)==0)
return strcmp( ((Excle)a).id,((Excle)b).id );
el
return strcmp(((Excle)a).name,((Excle)b).name);
}
百年孤独英文
int comp3(const void a,const void b)
{
if((((Excle)a).score)==(((Excle)b).score))
return strcmp(((Excle)a).id,((Excle)b).id);
el return ((Excle)a).score-((Excle)b).score;
}
qsort的第4个参数是函数的名字,你写什么函数名,就调⽤对应的函数。我们按照不同的C值,调⽤不同的函数。
switch©{
ca 1:qsort(PX, n, sizeof(PX[0]), comp1); break;
ca 2:qsort(PX, n, sizeof(PX[0]), comp2); break;
ca 3:qsort(PX, n, sizeof(PX[0]), comp3); break;
}
当C=1时,执⾏qsort(PX, n, sizeof(PX[0]), comp1),传给qsort函数的⽐较函数名为comp1,qsort函数在⽐较两个元素的⼤⼩时,就会调⽤comp1。当C=2时,执⾏qsort(PX, n, sizeof(PX[0]), comp2),qsort就会调⽤comp2。
完整的代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
成语谜语和答案typedef struct Excle{
char id[7];
char name[9];
int score;
}Excle;
int comp1(const void a,const void b)
电脑派位
{
return strcmp(((Excle)a).id,((Excle)b).id);
}
int comp2(const void a,const void b)
{
if(strcmp(((Excle)a).name,((Excle)b).name) == 0)
return strcmp(((Excle)a).id,((Excle)b).id);
摄氏度el
return strcmp(((Excle)a).name,((Excle)b).name);
}
int comp3(const void a,const void b)
{
班组名称
if((((Excle)a).score)==(((Excle)b).score))return strcmp(((Excle)a).id,((Excle)b).id);
el return ((Excle)a).score-((Excle)b).score;
}
int main(){
int i,n,c;
scanf("%d %d",&n,&c);
Excle PX[n];
for(i=0;i<n;i++)
{
描写月季花的诗句scanf("%s %s %d",PX[i].id,PX[i].name,&PX[i].score); }
switch©{
ca 1:qsort(PX,n,sizeof(PX[0]),comp1);break;
ca 2:qsort(PX,n,sizeof(PX[0]),comp2);break;
ca 3:qsort(PX,n,sizeof(PX[0]),comp3);break;
}
for(i=0;i<n;i++)
{
printf("%s %s %d\n",PX[i].id,PX[i].name,PX[i].score); }
}