C语言qsort函数介绍

前言

学到了函数指针,那这篇博客我们可以根据函数指针,了解一个函数qsort的应用与模拟实现

欢迎关注个人主页:小张同学zkf

若有疑问   评论区见


目录

 

1.回调函数

2.qsort函数使用

3.qsort模拟实现


1.回调函数

讲这个东西之前我们来认识一下回调函数,回调函数就是一个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数
时,被调用的函数就是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

简单来说,就是把调用的函数的地址以参数的形式传递过去,使用函数指针接收,函数指针指向什么函数就调用什么函数,这就是回调函数的功能。
 

2.qsort函数使用

qsort是一个库函数,函数功能就是快速排序,可以将数组里的东西按照一定的顺序升序降序排列

qsort的形式:

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

我们来一个一个分析,第一个参数是指针指向的是待排序数组的第一个元素,由于不知道你会传递什么指针类型,就用void*(是无具体类型的指针,它的作用就是接收任何类型的地址)暂时来作为指针类型。第二个参数是base指向的待排序数组的元素个数,第三个参数是每个元素的大小(字节长度),最后一个传入的是一个函数compar的指针,指向的是两个元素的比较函数。

举一个例子:

#include <stdio.h>
//qosrt函数的使⽤者得实现⼀个⽐较函数
int int_cmp(const void * p1, const void * p2)
{
return (*( int *)p1 - *(int *) p2);
}
int main()
{
int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
int i = 0;
qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
{
printf( "%d ", arr[i]);
}
printf("\n");
return 0;
}

这个是排序整形数据,我们可以看到,arr首元素地址,元素个数,每个元素的大小,以及比较方式都传入了qsort函数,这个比较函数int_cmp,在qsort函数内部调用,返回的是整型类型的两个元素的整型差,根据结果推断,如果第一个元素比第二个元素大,返回的是大于零的数,按的是升序排列,如果返回时两个元素交换位置,返回的若是大于零的数,则是降序排列

那如果不是整型数组能排列吗,我要是结构体类型那

struct Stu //学⽣
{char name[20];//名字int age;//年龄
};
//假设按照年龄来⽐较
int cmp_stu_by_age(const void* e1, const void* e2)
{return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
//strcmp - 是库函数,是专⻔⽤来⽐较两个字符串的⼤⼩的
//假设按照名字来⽐较
int cmp_stu_by_name(const void* e1, const void* e2)
{return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
//按照年龄来排序
void test2()
{struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };int sz = sizeof(s) / sizeof(s[0]);qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}
//按照名字来排序
void test3()
{struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };int sz = sizeof(s) / sizeof(s[0]);qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}
int main()
{test2();test3();return 0;
}

可以看到结构体类型也可以按照某个成员类型来排列数组。

所以若使用qsort函数快速排序时这个比较函数必须自己定义好比较方式,然后把它的地址作为参数传入qsort内部进行调用。就能实现qsort快速排序的功能。

3.qsort模拟实现

那这个函数内部到底是怎么实现的那,qsort函数是快速排序的功能,那我们可以先想想我之前发的指针博客里的冒泡排序。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void maopao(int arr[], int x)
{for (int i = 0; i < x-1; i++)//趟数{int s = 1;//假设有序for (int j = 0; j <x-i-1 ; j++)//一趟比较几次{if (arr[j]>arr[j+1]){int tmp = arr[j];arr[j] = arr[j + 1];arr[j+1] = tmp;s = 0;//不完全有序}}if (s == 1)//以完全有序{break;}}
}
int main()
{int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);maopao(arr, sz);int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}

两个for循环,外部那个for循环代表一共要比较几趟,如果10个数比较9趟,11个数比较10趟可见最后一个数不需要再走,内部那个for循环代表一趟要比较几次,第一个数比较,除了自身以外比较元素个数减1次,第二个数开始比较时由于已经和第一个数比较过了,就不需要再和它比较,所以比较次数再减1,所以随着趟数的增加,比较次数依次减小,直到完全有序。

那我们想想,qsort内部是不是也是类似于冒泡排序那,我们来模拟一下


void qsort_s(void* a, size_t x, size_t whits, int (*campare)(const void* as1, const void* as2))
{//趟数for (int i = 0; i < x-1; i++){for (int j = 0; j < x - i - 1; j++)//每一趟{if (campare((char*)a + j*whits, (char*)a+(j+1)*whits) > 0)//满足交换的条件{swap((char*)a + j * whits, (char*)a + (j + 1) * whits,whits);//内部一个单元一个单元的交换}}}}
void print(int arr[], int x)
{for (int i = 0; i < x; i++){printf("%d ",arr[i]);}
}
int main()
{int arr[] = { 23,5,67,89,36,2,300};int sz = sizeof(arr) / sizeof(arr[0]);qsort_s(arr, sz, sizeof(arr[0]), campare);print(arr, sz);return 0;
}

我们假设qsort_s是模仿qsort的函数,qsort不仅仅可以排列整型,一切类型都可以排序,所以元素和下一个元素之间的字节宽度我们要考虑到,就是第三个参数whits,两次for循环内部是一个判断条件,就是比较函数返回值是否大于零,大于零就交换,也就是传入这个函数的地址是为了在此处调用比较函数进行判断是否达成交换的条件,在判断的时候不管元素地址是什么类型都要将它强制转换成char*类型,是因为我们交换这俩元素,这俩元素不管什么类型都是俩内存块,内存块交换要内部一个字节一个字节交换,从而达到两个元素的整体交换。

从这里我们也看出qsort函数内部模仿原理跟冒泡排序有点类似。

我们在定义比较函数时,也要注意,传过来的元素地址不管什么类型,都要将它强制转换成整型指针,这是因为因为比较函数返回值就是整型。以下是比较函数的定义

int campare(const void* as1, const void* as2)
{return *(int*)as1 - *(int*)as2;}

别忘了定义交换函数

void swap(char* cv1, char* cv2, size_t whits)
{for (int i = 0; i < whits; i++)//一一对应交换{char tmp = *cv1;*cv1 = *cv2;*cv2 = tmp;cv1++;cv2++;}
}

以下是qsort函数模拟实现的所有代码 

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int campare(const void* as1, const void* as2)
{return *(int*)as1 - *(int*)as2;}
void swap(char* cv1, char* cv2, size_t whits)
{for (int i = 0; i < whits; i++)//一一对应交换{char tmp = *cv1;*cv1 = *cv2;*cv2 = tmp;cv1++;cv2++;}
}
void qsort_s(void* a, size_t x, size_t whits, int (*campare)(const void* as1, const void* as2))
{//趟数for (int i = 0; i < x-1; i++){for (int j = 0; j < x - i - 1; j++)//每一趟{if (campare((char*)a + j*whits, (char*)a+(j+1)*whits) > 0)//满足交换的条件{swap((char*)a + j * whits, (char*)a + (j + 1) * whits,whits);//内部一个单元一个单元的交换}}}}
void print(int arr[], int x)
{for (int i = 0; i < x; i++){printf("%d ",arr[i]);}
}
int main()
{int arr[] = { 23,5,67,89,36,2,300};int sz = sizeof(arr) / sizeof(arr[0]);qsort_s(arr, sz, sizeof(arr[0]), campare);print(arr, sz);return 0;
}

 

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

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

相关文章

mq基础类设计

消息队列就是把阻塞队列这样的数据结构单独提取成一个程序独立进行部署。——>实现生产者消费者模型。 但是阻塞队列是在一个进程内部进行的&#xff1b; 消息队列是在进程与进程之间进行实现的&#xff0c; 解耦合&#xff1a;就是在分布式系统中&#xff0c;A服务器调用B…

Tomcat -2

① 单机反向代理 7-2 代理服务器 7-5 tomcat 设置 7-3 测试&#xff1a; 代理服务器那里写什么就显示什么 ② 多机反向代理 实现动静分离和负载均衡 7-2 nginx 7-3 7-5 测试&#xff1a; 看静态&#xff1a; 看动态&#xff1a; ③ 反向代理多机多级 7-2 7-1 和 7-4 7-3…

【STM32】STM32学习笔记-读写内部FLASH 读取芯片ID(49)

00. 目录 文章目录 00. 目录01. FLASH概述02. 读写内部FLASH接线图03. 读写内部FLASH相关API04. 读写内部FLASH程序示例05. 读写芯片ID接线图06. 读写芯片ID程序示例07. 程序示例下载08. 附录 01. FLASH概述 STM32F10xxx内嵌的闪存存储器可以用于在线编程(ICP)或在程序中编程(…

华为OD机试 - 数字排列 - 深度优先搜索dfs算法(Java 2024 C卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述1、输入2、输出3、说明 四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&a…

外部存储器接口(EMIF)

外部存储器接口&#xff08;EMIF&#xff09; 该设备支持双核架构&#xff1b;为了为每个CPU子系统提供一个专用的EMIF&#xff0c;该设备支持两个EMIF模块——EMIF1和EMIF2。两个模块完全相同&#xff0c;具有相同的功能集&#xff0c;但具有不同的地址/数据大小。EMIF1在CPU…

Unity 角色控制(初版)

角色控制器组件&#xff0c;当然是将组件放在角色上了。 using System.Collections; using System.Collections.Generic; using UnityEngine;public class c1 : MonoBehaviour {// 获取角色控制器private CharacterController player;void Start(){// 加载角色控制器player …

机器学习 | 模型性能评估

目录 一. 回归模型的性能评估1. 平均平方误差(MSE)2. 平均绝对误差(MAE)3. R 2 R^{2} R2 值3.1 R 2 R^{2} R2优点 二. 分类模型的性能评估1. 准确率&#xff08;Accuracy&#xff09;2. 召回率&#xff08;Recall&#xff09;3. 精确率&#xff08;Precision&#xff09;4. …

大模型学习笔记五:RAG

文章目录 一、RAG介绍1)局限性2)通过检索增强生成二、RAG系统的基本搭建流程1)搭建流程简介2)文档的加载和切割3)检索引擎4)LLM接口封装5)prompt模板6)RAG Pipeline初探7)关键字检索局限性三、向量检索1)文本向量2)向量相似度计算3)向量数据库4)基于向量检索的RAG…

电源完整性设计的重要三步!

电源模块布局布线 电源模块是电子设备的能量来源&#xff0c;其性能与布局直接影响到整个系统的稳定性和效率。正确的布局和走线不仅能减少噪声干扰&#xff0c;还能确保电流的顺畅流通&#xff0c;从而提高整体性能。 1、电源模块布局 ● 源头处理&#xff1a;电源模块作为…

Java 面试题

Java 基础 以下代码执行结果&#xff1f; 示例1&#xff1a; public static void main(String[] args) {int a 0;Integer b 0;String c "0";String d new String("0");change(a, b, c, d);System.out.println(a "|" b "|" …

glibc

交叉编译器的glibc库位置&#xff1a; 用此交叉编译器编译的根文件系统对应的开发板上的GLIBC版本&#xff1a; 证明buildroot会使用交叉编译器自带的glibc库来对根文件系统进行编译。

如何在CentOS部署JumpServer堡垒机并实现无公网ip环境远程访问

文章目录 前言1. 安装Jump server2. 本地访问jump server3. 安装 cpolar内网穿透软件4. 配置Jump server公网访问地址5. 公网远程访问Jump server6. 固定Jump server公网地址 前言 JumpServer 是广受欢迎的开源堡垒机&#xff0c;是符合 4A 规范的专业运维安全审计系统。JumpS…

通义千问1.5(Qwen1.5)大语言模型在PAI-QuickStart的微调与部署实践

作者&#xff1a;汪诚愚&#xff08;熊兮&#xff09;、高一鸿&#xff08;子洪&#xff09;、黄俊&#xff08;临在&#xff09; Qwen1.5&#xff08;通义千问1.5&#xff09;是阿里云最近推出的开源大型语言模型系列。作为“通义千问”1.0系列的进阶版&#xff0c;该模型推出…

【鸿蒙 HarmonyOS 4.0】登录流程

一、背景 登录功能在应用中是一个常用模块&#xff0c;此次使用 HarmonyOS 实现登录流程&#xff0c;包含页面呈现与网络请求。 二、页面呈现 三、实现流程 3.1、创建项目 构建一个ArkTS应用项目(Stage模型)&#xff0c;今天创建流程可查看官网教程&#xff1a;文档中心 目…

llc半桥开关电源基础知识2(电路图简化)

llc半桥开关电源拓扑图如下 稳态:LLC电源已经正常工作,已经输出电压稳定稳态:LLC电源已经正常工作,已经输出电压稳定。 我们在分析拓扑结构的时候,都是基于他已经正常稳定输出的时候来分析的,毕竟LC电源只要以工作啊,绝大多数时间都是工作在稳态。 具体电路图化简分析如…

yolov9训练

目录 说明 1、下载代码安装新的python环境 2、准备数据 3、修改代码 说明 本文参考该博主的文章&#xff0c;在已经有数据的情况&#xff0c;进行简单总结。需要详细版见原文链接如下&#xff1a;YOLOV9保姆级教程-CSDN博客 1、下载代码安装新的python环境 代码下载&…

力扣每日一道系列 --- LeetCode 160. 相交链表

&#x1f4f7; 江池俊&#xff1a; 个人主页 &#x1f525;个人专栏&#xff1a; ✅数据结构探索 ✅LeetCode每日一道 &#x1f305; 有航道的人&#xff0c;再渺小也不会迷途。 LeetCode 160. 相交链表 思路&#xff1a; 首先计算两个链表的长度&#xff0c;然后判断两个链…

如何选择程序员职业赛道

目录 前言1 个人技能分析1.1 技术栈评估1.2 经验积累1.3 数据科学能力 2 兴趣与价值观2.1 用户交互与界面设计2.2 复杂问题解决与系统优化 3 长期目标规划4 市场需求分析4.1 人工智能和云计算4.2 前沿技术趋势 5 就业前景5.1 前端在创意性公司中的应用5.2 后端在大型企业中的广…

前端爬虫+可视化Demo

爬虫简介 可以把互联网比做成一张 “大网”&#xff0c;爬虫就是在这张大网上不断爬取信息的程序。 爬虫是请求网站并提取数据的自动化程序。 省流&#xff1a;Demo实现前置知识&#xff1a; JS 基础Node 基础 &#xff08;1&#xff09;爬虫基本工作流程&#xff1a; 向…

『运维备忘录』之 RegEx 正则表达式实例汇总

运维人员不仅要熟悉操作系统、服务器、网络等知识&#xff0c;甚至对于开发相关的也要有所了解。很多运维工作者可能一时半会记不住那么多命令、代码、方法、原理或者用法等等。这里我将结合自身工作&#xff0c;持续给大家更新运维工作所需要接触到的知识点&#xff0c;希望大…