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 %d\n",PX[i].id,PX[i].name,PX[i].score);
}
}

qsort函数用法详解相关推荐

  1. ROW_NUMBER() OVER()函数用法详解 (分组排序 例子多)

    ROW_NUMBER() OVER()函数用法详解 (分组排序 例子多) https://blog.csdn.net/qq_25221835/article/details/82762416 post ...

  2. C++中substr()函数用法详解

    C++中substr()函数用法详解 原型: string substr (size_t pos = 0, size_t len = npos) const; 返回一个新构造的string对象,其值初 ...

  3. LayoutInflater的inflate函数用法详解

    LayoutInflater的inflate函数用法详解 LayoutInflater作用是将layout的xml布局文件实例化为View类对象. 获取LayoutInflater的方法有如下三种: ...

  4. c++ memset 语言_C++中memset函数用法详解

    本文实例讲述了C++中memset函数用法.分享给大家供大家参考,具体如下: 功 能: 将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值,块的大小由第三个参数指定,这个函数通常 ...

  5. mysql: union / union all / 自定义函数用法详解

    mysql: union / union all http://www.cnblogs.com/wangyayun/p/6133540.html mysql:自定义函数用法详解 http://www. ...

  6. python中mat函数_Python中flatten( )函数及函数用法详解

    flatten()函数用法 flatten是numpy.ndarray.flatten的一个函数,即返回一个一维数组. flatten只能适用于numpy对象,即array或者mat,普通的list列 ...

  7. ROW_NUMBER() OVER()函数用法详解

    今天同事问了一个关于插入表的问题,对象:被插入表sys_equi_disorg   A  , 查询表sys_equi_dict   B 因为A表的ID不是自增的,并且不能更改表结构,主键默认值还是0, ...

  8. C++ search()函数用法详解(深入了解,一文学会)

    find_end() 函数用于在序列 A 中查找序列 B 最后一次出现的位置.那么,如果想知道序列 B 在序列 A 中第一次出现的位置,该如何实现呢?可以借助 search() 函数. search( ...

  9. C++ reverse()函数用法详解(深入了解,一文学会)

    reverse_copy() 算法可以将源序列复制到目的序列中,目的序列中的元素是逆序的.定义源序列的前两个迭代器参数必须是双向迭代器.目的序列由第三个参数指定,它是目的序列的开始迭代器,也是一个输出 ...

最新文章

  1. 虚拟私有云网络VPC
  2. 编程软件python中的if用法-Python高效编程的19个技巧
  3. 设计模式之反射与配置文件
  4. 新唐M0 KEIL环境搭建,找不到device不识别,关键:Nu-Link_Keil_Driver
  5. leetcode117. 填充每个节点的下一个右侧节点指针 II
  6. 蓝懿IOS委托模式代理模式
  7. Oracle使用sys用户exp备份数据
  8. Composer Player 属性设置
  9. python迭代器的设计
  10. 修改docker网桥模式下的网络地址
  11. 将excel表数据顺序与linux,Excel中表格数据进行颠倒顺序的设置方法
  12. Discuz 模板目录
  13. Word2010下划线不显示
  14. MLB的选秀会有哪些规定和流程·棒球6号位
  15. python学习笔记(二)--深入了解python函数
  16. 20元一支的洗面奶,7天卖了上万,他们是如何做到的?
  17. Python学习笔记(八)—切片(slicing)
  18. html怎么写分享代码,怎么写一个文件分享网页?百度一键分享按钮HTML代码
  19. 二叉树遍历之中序遍历算法(非递归、递归)入门详解
  20. echarts 重新渲染数据

热门文章

  1. 给出初始参数和轨道用matlab,轨道电路MATLAB仿真及应用+源码
  2. TouchSlide触屏滑动特效插件的使用
  3. JMM与volatile
  4. CACL联赛第一赛季第一轮比赛排名公布!
  5. 计算机知识点记不住怎么办,学完一科忘一科,记不住知识点怎么办?
  6. 利用普通充电器给三星平板电脑充电的改造
  7. k910 Android 5,5英寸海思K910T 华为Ascend P7全新发布
  8. 你的数据“可视化”了吗?
  9. GitHub团队协同开发流程
  10. 安装完tensorflow后还提示“no moudle named tensorflow”