深入解析 qsort 排序(上),它为什么是万能排序?

        前言:对于库函数有适当了解的朋友们,对于 qsort 函数想必是有认知的,因为他可以对任意数据类型进行排序的功能属实是有点厉害的,本次分享,笔者就给大家带来 qsort 函数的全面的解读

        本次知识的分享笔者分为上下俩卷文章进行讲解,在本篇文章中,给大家带来 qsort 函数的基础认知,以及他如何进行功能实现的背后原理讲解


目录

一.什么是 qsort 函数

二. qsort 的使用 

1.对于整形数组的排序

2.对于浮点型数组的排序 

3.对于结构体数组的排序

通过姓名首字母排序

通过年龄进行排序


一.什么是 qsort 函数

我们打开 cplusplus 网站查看详细定义

qsort - C++ Reference (cplusplus.com)

在官方的定义中是这样说的:

  • 对所指向的数组元素进行排序,每个元素长度为字节,使用函数确定顺序
  • 此函数使用的排序算法通过调用指定的函数,将指向元素的指针作为参数来比较元素对
  • 该函数不返回任何值,但通过重新排序所定义的元素来修改所指向数组的内容
  • 等效元素的顺序未定义

        翻译成我们自己的语言就是,这个函数是用来对数组元素排序的我们自己用函数来确定排序的规则 ,并且对于数组元素的类型是没有作任何要求的,这也就意味着,我们可以对任意类型的元素进行任意规则的排序

        还是看上图,这个函数有 4 个参数,并且不进行返回值,我们接下来分别解析一下具体是怎么样的一个意思:

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

 

对于这样的文本,我们还是要转化为自己好理解的方式:

  1. void* base:待排序数组的第一个元素的地址
  2. size_t num:待排序数组的元素个数
  3. size_t size:待排序数组中一个元素的大小
  4. int (*compar)(const void*, const void*):函数指针-cmp指向了一个函数,这个函数是用来比较两个元素的,e1和e2中存放的是需要比较的两个元素的地址

        另外,对于第 4 点,也就是具体比较方法的函数,它的返回值是需要注意的,只返回三类值,大于零,等于零,小于零,具体含义在后文实例中进行讲解,这里就不再多做赘述

二. qsort 的使用 

1.对于整形数组的排序

我们定义一个整型数组

	int arr1[10] = { 0,1,2,3,4,5,6,7,8,9 };

然后定义一个函数打印数组方便我们观察

void print1(int* arr)
{for (int i = 0; i < 10; i++){printf("%d ", *(arr + i));}printf("\n");
}

然后我们调用 qsort 进行排序,在这里我们详细的看看四个参数:

  • 第一个参数 arr1,指向数组arr1,我们就得到了需要排序的数组的第一个元素的地址
  • 第二个参数 10,代表的是这个数组的大小是 10
  • 第三个参数 4,代表的是数组元素的字节大小,arr1 是个整形数组,整形 int 是 4 个字节,所以这里的参数是4
  • 第四个参数 comp_int,这是个函数,更是它这个函数本身的地址,我们将这里的函数地址作为参数,就可以调用我们的判断规则的函数
qsort(arr1, 10, 4, comp_int);
int comp_int(const void* a,const void* b)
{return *(int*)b - *(int*)a;
}

        判断函数这里更是我们需要注意的地方,我们进入到判断函数中,这个判断函数又有俩个参数,都是 void* 的类型,这样设定的目的是为了拿到一个地址,为了泛型编程的思想,我们这里只管拿到地址,不需要思考怎么处理这俩个地址,所以使用 void* 这样就能满足更多的需求,后期我们想要更改判断条件的话,也只需要更改这个函数内部的数据,不影响我们整体传参,这是一种非常高级的编程思想,我们可以进行适当的学习,同时在公司内部编程的时候,往往是很多人进行编程一个程序,这样设置更是为了方便这种团队编程的思想和功能

        进入函数体,我们首先先确定我们的具体需求,我们要的是俩个整数进行比较,但是这里我们拿到的地址是 void* 类型的,所以我们需要进行强制转化,把俩个参数转化为整形,这样才能满足我们的需求,拿到整形地址后对其解引用就可以访问里面的数据然后进行判断了,这里我们设置的是如果 b>a 就返回正值,也就是后面的数字大于前面的数字的情况下,我们才进行交换

        输出结果实例如下,我们可以看见,对于原本升序的数组,经过我们的排序后,就变成了降序

	int arr1[10] = { 0,1,2,3,4,5,6,7,8,9 };print1(arr1);qsort(arr1, 10, 4, comp_int);print1(arr1);

2.对于浮点型数组的排序 

         和刚才整形数组的排序类似,我们定义一个浮点型数组,然后在排序前和排序后分别进行打印一次,我们这里需要重点观察的是他的判断函数,也就是他的第四个参数

	float arr2[5] = { 1.2,3.4,5.6,7.8,9.9 };print2(arr2);qsort(arr2, 5, sizeof(float), comp_float);print2(arr2);

        我们观察判断函数,我们发现对于 void* 类型的转化为浮点型指针,然后进行解引用访问,我们试着运行一下看看可不可以这样写

int comp_float(const void* a, const void* b)
{return *(float*)b - *(float*)a;
}

        经过实验,我们发现 void* 类型强制转化为浮点型也是可以正常运行的,这里也就验证了我们刚才说过的泛型编程的思想 

 

3.对于结构体数组的排序

        刚才简单的数据类型我们都实验过了,我们好像可以发现这样设置判断函数非常的方便,参数内不管接受什么数据类型,统一写 void* 类型就可以了,那我们现在不妨试一些复杂的数据类型 —— 结构体数组

        在一切实验之前,我们先得写一个结构体类型,还有其对于的结构体数组,我们写个学生的结构体,内容由学生姓名和年龄组成

struct stu
{char name[20];int age;
};

然后对于结构体数组,我们分别如下定义,张三19岁,李四20岁,王五21岁

struct stu arr3[] ={{"zhangsan",19},{"lisi",20},{"wangswu",21}};

        做好一切准备工作后,我们进行 qsort 排序的设置,我们可以分为俩种方式排序,一种是用姓名首字母进行排序,一种是由年龄大小进行排序

通过姓名首字母排序

我们如下设置,我们来看看 qsort 的四个参数:

  • 第一个参数 arr3,指向的是结构体数组的第一个元素的地址
  • 第二个参数 3,代表的是这个结构体数组一共有 3 个元素
  • 第三个参数 sizeof(arr3[0]),代表的是结构体数组的每一个元素的大小,这一个元素包含了学生的姓名和年龄
  • 第四个参数 comp_name,代表的是排序的规则函数
	qsort(arr3, 3, sizeof(arr3[0]), comp_name);
int comp_name(const void* a, const void* b)
{return strcmp(((struct stu*)a)->name, ((struct stu*)b)->name);
}

        我们还是重点看一下排序规则的函数,参数方面还是和我们上面讲的一样,俩个 void* 方便我们后期修改,进入函数体,我们将俩个参数强制转化为结构体类型,然后分别指向结构体内部的姓名变量;然后我们调用了  strcmp 函数,这个函数的功能是比较俩个字符串的首元素的 ACSSII 大小,然后进行返回值,返回值和外边的比较方法的返回值的格式和大小一模一样,如下图

通过年龄进行排序

相较于上述的姓名排序,这里的年龄排序就简单的多了

	qsort(arr3, 3, sizeof(arr3[0]), comp_age);
int comp_age(const void* a, const void* b)
{return ((struct stu*)b)->age - ((struct stu*)a)->age;
}

        我们这里对年龄整体的处理和上述的整形数组的处理是一样的,唯一不同的就是我们拿到数据的方式,我们先将其强制转换为结构体类型,然后通过结构体类型指向的年龄进行比较


        通过了上述的实例讲述,我们明白了,对于任何的数据类型,我们在拿这个参数的时候,都是用 void* 类型进行取址,然后对于具体比较操作的时候,我们再将其进行具体的操作,强制转化为我们需要的数据类型,然后进行排序,这样就实现了其 “万能排序” 的功能

下一篇文章我们就探讨如何对 qsort 函数的模拟实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/77864.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网工内推 | 国企网络运维,大专以上即可,有厂商认证优先

01 北京新明星电子技术开发有限公司 招聘岗位&#xff1a;运维工程师 职责描述&#xff1a; 1、负责所在特定客户的技术支持服务工作&#xff0c;负责各厂商服务器、存储设备的日常操作、系统升级、故障处理工作。 2、对运维工作进行总结、提出问题或隐患&#xff0c;给出建议…

类和对象:构造函数,析构函数与拷贝构造函数

1.类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真的什么都没有吗&#xff1f;并不是&#xff0c;任何类在什么都不写时&#xff0c;编译器会自动生成以下6个默认成员函数。 默认成员函数&#xff1a;用户没有显式实现&#xff0c;编译器…

C++位图

一、前提引入 思考如何实现下面的题目 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在 这40亿个数中。【腾讯】 一个整型数据int为4个字节&#xff0c;计算得 &#xff0c;大约需要16G的存储空间。如果采用遍历判断…

01-从JDK源码级别剖析JVM类加载机制

上一篇&#xff1a;JVM虚拟机调优大全 1. 类加载运行全过程 当我们用java命令运行某个类的main函数启动程序时&#xff0c;首先需要通过类加载器把主类加载到JVM。 public class Math {public static final int initData 666;public static User user new User();public i…

论文阅读 - Outlier detection in social networks leveraging community structure

目录 摘要 1. Introduction 2. Related works 3. Preliminaries 3.1. 模块化度量 3.2. Classes of outliers 3.2.1. 点异常 3.2.2. Contextual anomalies 3.2.3. Collective anomalies 3.3. Problem definition 3.4. Outliers score 4. Methodology 4.1. Proposed appr…

初识FUSE(Filesystem in userspace)

初识FUSE&#xff08;Filesystem in userspace&#xff09; 什么是FUSE&#xff1f;FUSE原理FUSE协议 如何去使用&#xff1f;参考文章 之前因为一次作业有幸接触过FUSE,觉得它是一个很不错的框架&#xff0c;没来得及仔细了解。现在有点时间了&#xff0c;想要利用它做一个文件…

Linux部署kettle并设置定时任务

一.安装Kettle linux中使用kettle时首先需要jdk环境&#xff0c;这里就不概述linux中jdk的安装与配置了。 1.首先将kettle压缩包放入linux并解压 unzip data-integration.zip kettle安装路径为:/root/Kettle9.3/data-integration 设置权限 chmod -R 755 /root/Kettle9.3/d…

LVS负载均衡群集NAT模式

LVS负载均衡群集NAT模式 一、集群与分布式1.1、集群的含义1.2、lvs模型1.3、系统性能扩展方式1.4、群集的三种类型1.4.1、负载均衡群集1.4.2、高可用群集1.4.3、高性能运算群集 1.5、LVS的负载调度算法1.5.1、轮询1.5.2、加权轮询1.5.3、最少连接1.5.4、加权最少连接1.5.5、ip_…

pc端测试手机浏览器运行情况,主要是测试硬件功能

测试h5震动摇晃等功能时不方便测试&#xff0c;需要连电脑显示调试数据 方法&#xff1a; 1.需要手机下载谷歌浏览器&#xff0c;pc端用edge或这谷歌浏览器 2.手机打开USB调试&#xff0c;打开要测试的网页 3.pc端地址栏输入edge://inspect/#devices&#xff08;这里用的edge浏…

K8S:kubeadm搭建K8S+Harbor 私有仓库

文章目录 一.部署规划1.主机规划2.部署流程 二.kubeadm搭建K8S1.环境准备2.安装docker3. 安装kubeadm&#xff0c;kubelet和kubectl4.部署K8S集群&#xff08;1&#xff09;初始化&#xff08;2&#xff09;部署网络插件flannel&#xff08;3&#xff09;创建 pod 资源 5.部署 …

Linux HTTP协议

目录 1.浏览器与服务器通信过程2.HTTP请求报头&#xff08;1&#xff09;HTTP的请求报头结构&#xff08;2&#xff09;HTTP的请求方法 3.HTTP应答报头&#xff08;1&#xff09;HTTP的应答报头结构&#xff08;2&#xff09; HTTP的应答状态 1.浏览器与服务器通信过程 浏览器…

回归预测 | MATLAB实现PSO-SDAE粒子群优化堆叠去噪自编码器多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现PSO-SDAE粒子群优化堆叠去噪自编码器多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现PSO-SDAE粒子群优化堆叠去噪自编码器多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效果一览…

清理Maven仓库中下载失败的文件

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

Nginx详解 五:反向代理

文章目录 1. 正向代理和反向代理1.1 正向代理概述1.1.1 什么是正向代理1.1.2 正向代理的作用1.1.3 正向代理的基本格式 1.2 反向代理概述1.2.1 什么是反向代理1.2.2 反向代理可实现的功能1.2.3 反向代理的可用模块 2. 配置反向代理2.1 反向代理配置参数2.1.1 proxy_pass2.1.2 其…

当你的公司突然开始大量的裁员,被留下的你,真的准备好面对以后了吗?

留下来的&#xff0c;也是迷茫的 最近公司突然开始大量裁员&#xff0c;裁了一多半&#xff0c;作为唯一留下的APP 端开发人员&#xff0c;也开始陷入了焦虑&#xff0c;开始了思考&#xff0c;未来究竟何去何从&#xff0c;是否再去转到原生&#xff0c;从事原生的开发工作&a…

C语言入门Day_19 初识函数

目录 1.函数的定义 2.函数的调用 3.易错点 4.思维导图 前言&#xff1a; printf()我们已经很熟悉了&#xff0c;它有一个特定的功能&#xff0c;就是在屏幕上输出一行文字。之前的课程我们都称呼printf()为一个功能&#xff0c;实际上ta在编程中有个特定的名字——函数。 …

测试-----selenuim webDriver

文章目录 1.页面导航2.元素定位3. 浏览器操作4.获取元素信息5. 鼠标的操作6. 键盘操作7. 元素等待8.下拉框9.弹出框10.滚动条11.frame处理12.验证码处理&#xff08;cookie&#xff09; 1.页面导航 首先是导入对应的包 :from selenium import webdriver然后实例化:driver web…

网络安全宣传周|探索AI数字人的魅力和价值所在

9月11日至9月17日是国家网络安全宣传周&#xff0c;在福州举办的安全博览会上有着多种人工智能模型产品亮相现场&#xff0c;吸引着众多参观者的目光&#xff0c;尤其是AI数字人面对不同的问题、不同的场景都可以进行实时响应&#xff0c;不同于冷冰冰的传统智能客服的对话场景…

前端面试合集(二)

前端面试题合集 1.懒加载的原理及实现了解吗2.如何理解JS异步3.阐述一下 JS 的事件循环4.JS 中的计时器能做到精确计时吗&#xff1f;为什么&#xff1f; 1.懒加载的原理及实现了解吗 原理&#xff1a;当图片没有到达可视范围内时&#xff0c;图片不加载&#xff0c;当图片一旦…

Mobileye CEO来华:只有能控制住成本的公司,才能活下来

‍作者|德新 编辑|王博 上午9点近一刻&#xff0c;Mobileye CEO Amnon Shuashua步入酒店的会议室。由于Amnon本人是以色列希伯来大学的计算机科学教授&#xff0c;大部分人更习惯称他为「教授」。 时近以色列的新年&#xff0c;这趟教授的中国之行安排十分紧凑。 他率领了一…