首页 > 编程知识 正文

qsort函数怎么用,qsort函数实现

时间:2023-05-05 22:42:43 阅读:263128 作者:4217

qsort()函数用法详解

limabean
本文版权归作者所有,欢迎转载,但未经作者同意必须在文章页面给出原文链接,否则保留追究法律责任的权利。

1.qsort函数简介

排序是程序员经常碰到的问题,数据结构与算法的教科书上已经有许多标准的排序算法。在每次需要排序的时候,都要按照教科书重写一次代码?C语言早就考虑到了这个问题,已经提前编写好了一个快速排序的库函数,就是qsort。直接调用一下库函数,很简单对吗?试试看,哇,根本不知道怎么调用。
qsort是C标准库<stdlib.h>库中的函数,使用时引入#include <stdlib.h>。
qsort函数的原型描述为:
void qsort( void *base, 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 voida, const voidb)
{
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 voida,const voidb){
return strcmp( ((Excle)a).id,((Excle)b).id);
} 比较学号

int comp2(const voida,const voidb){
if (strcmp(((Excle)a).name,((Excle)b).name)==0)
return strcmp( ((Excle)a).id,((Excle)b).id );
else
return strcmp(((Excle)a).name,((Excle)b).name);
}

int comp3(const voida,const voidb)
{
if((((Excle)a).score)==(((Excle)b).score))
return strcmp(((Excle)a).id,((Excle)b).id);
else return ((Excle)a).score-((Excle)b).score;
}
qsort的第4个参数是函数的名字,你写什么函数名,就调用对应的函数。我们按照不同的C值,调用不同的函数。
switch©{
case 1:qsort(PX, n, sizeof(PX[0]), comp1); break;
case 2:qsort(PX, n, sizeof(PX[0]), comp2); break;
case 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 voida,const voidb)
{
return strcmp(((Excle)a).id,((Excle)b).id);
}
int comp2(const voida,const voidb)
{
if(strcmp(((Excle)a).name,((Excle)b).name) == 0)
return strcmp(((Excle)a).id,((Excle)b).id);
else
return strcmp(((Excle)a).name,((Excle)b).name);
}
int comp3(const voida,const voidb)
{
if((((Excle)a).score)==(((Excle)b).score))return strcmp(((Excle)a).id,((Excle)b).id);
else 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©{
case 1:qsort(PX,n,sizeof(PX[0]),comp1);break;
case 2:qsort(PX,n,sizeof(PX[0]),comp2);break;
case 3:qsort(PX,n,sizeof(PX[0]),comp3);break;
}
for(i=0;i<n;i++)
{
printf("%s %s %dn",PX[i].id,PX[i].name,PX[i].score);
}
}

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。