C语言进阶指针(3) ——qsort的实现
大家好,我们今天来学习回调函数qsort的实现。
首先让我们打开cplusplus.com找到qsort函数。
我们看到这个函数就可以看到它的头文件和参数信息。
#include<stdlib.h>
void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));
让我们看看它的具体参数信息:
void* base:待排序数组第一个元素的地址
size_t num:待排序数组的元素个数
size_t size:待排序数组中一个元素的大小单位是字节
int (compar)(const void,const void*)):这里是一个函数指针compar,指向的是一个比较的函数,是用来比较两个元素的,里面要放置的是要比较的两个元素的地址
我们要学习这个函数的话,我们首先就得来复习下冒泡排序法,这里大家可以参考我之前解析冒泡排序法的博客,我这里就不给大家一 一介绍了。https://editor.csdn.net/md/?articleId=132266676访问这个网址就可以看到我之前写的关于冒泡排序法的博客了。
我们为什么学会了冒泡排序法依然qsort排序函数呢,那是因为这个函数的优点,它既可以给数组排序也可以给结构体进行排序,但是排序时要注意:
- 排序整型数组, 两个整型可以直接使用>比较
- 排序结构体数组,两个结构体的数据可能不能直接使用>比较也就是不同类型的数据,比较出大小,方法是有差异的
我们看到代码—>:
#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函数就可以了,首先我们给qsort函数传参,第一个传的就是数组中第一个元素的地址,第二个就是数组中的元素多少,我们用sizeof就可以计算出来sizeof(arr)计算的是整个数组的大小,而sizeof(arr[0])计算的是每个元素的大小,两个值相除那就是这个数组元素的个数,因为这是一个整形数组,所以我们第三个传参传每个元素的大小,只要传sizeof(int)就可以了,最后传的就是一个函数指针的地址了,而函数指针中第一个指针存放的地址当然就是数组首元素的地址,第二个指针存放的就是要和第一个相比较的元素的地址了,我们都了解了,那就来看看程序运行的结果吧。
我们现在已经学会使用了这个函数,那么我们该怎么进行模拟这个函数的使用呢,这就是我们今天要学习的重点了,我们今天就来分享如何用qsort函数对整型数组和结构体变量进行排序。
首先我们先来模拟实现整形数组升序的实现:
void print_arr(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}
void bubble_sort(void* base, size_t num, size_t size, int (*cmp)(const void* e1, const void*e2))
{//冒泡排序的趟数int i = 0;for (i = 0; i < num - 1; i++){//一趟冒泡排序int j = 0;for (j = 0; j < num - 1 - i; j++){if (arr[j] > arr[j + 1]){//交换int tmp=arr[j];arr[j]=arr[j+1];arr[j+1]=tmp;}}}
}void test1()
{int arr[] = { 0,1,2,3,4,5,6,7,8,9 };//升序//排序为降序int sz = sizeof(arr) / sizeof(arr[0]);print_arr(arr, sz);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);print_arr(arr, sz);}int main()
{
test();
return 0;
}
这里我们要注意的是: int (cmp)(const void e1, const void* e2)
e1是一个指针,存放了一个要比较的元素的地址 e2是一个指针,存放了一个要比较的元素的地址 e1指向的元素>e2指向的元素,返回>0的数字
e1指向的元素==e2指向的元素,返回0 e1指向的元素<e2指向的元素,返回<0的数字
我们首先定义数组,调用函数test(),就会跳转到函数test中,在这个函数中我们用sz表示数组元素的个数,求解的方法还是sizeof的老方法,我们在将它调用到函数print_arr中对原数组进行打印,再调用函数bubble_sort,在里面我们用循环嵌套进行冒泡排序进行打印就行了。
那我们怎么来实现结构体的排序呢,因为我们int (cmp)(const void e1, const void*
e2)这个函数指针中所有的指针都是void* 型,这就相当于一个垃圾桶,可以储存任意类型变量的地址,这对我们实现结构体的排序密不可分。
我们先定义个结构体变量:
struct Stu
{char name[20];//20int age;//4
};
void test2()
{struct Stu arr[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15}};int sz = sizeof(arr) / sizeof(arr[0]);//3bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}
int main()
{//整型数据/字符数据/结构体数据...//可以使用qsort函数对数据进行排序//测试bubble_sort,排序整型数据//test1();//测试bubble_sort,排序结构体的数据test2();return 0;
}
看到代码块我们发现结构体里面定义了两个类型的变量,一个是名字char型,一个是年龄int型,那么我们对它进行排序的方法就有两种,一种是按照名字排序,一种按照年龄排序。
int cmp_stu_by_age(const void* e1, const void*e2)
{return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}int cmp_stu_by_name(const void* e1, const void* e2)
{return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
要注意的是,我们这里指针的类型是void* 类型要强制转换。这是我们两种方法的比较函数,那我们如何进行交换呢,我们就看到我们的交换函数
void swap(char* buf1, char* buf2, size_t size)
{int i = 0;for (i = 0; i < size; i++){char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}swap((char*)base + j * size, (char*)base + (j + 1) * size, size)
我们看到这里的交换函数:
(char*)base + j * size :确定要交换的第一个数据的地址
(char*)base + (j + 1) * size :确定要交换的第二个数据的地址
size :确定这俩个数据直接隔了多少个字节,方便逐字节进行操作
因为我们的base是char*的指针里面存放的是结构体变量里首元素的地址,size表示的是每个元素的大小,j * size表示的就是跳过多少个元素,而两个元素之间的交换就是字节之间的交换,所以我们算出每个每个元素的大小,指针代表的char * 型的,是一个字节,所以两个元素进行一次交换是进行一个字节之间的交换,所以我们这里只需要用一个循环就可以实现两个元素的交换了。
接下来我们就来看看完整的代码:
#include <string.h>void print_arr(int arr[], int sz)
{int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}//int (*cmp)(const void* e1, const void* e2)
//e1是一个指针,存放了一个要比较的元素的地址
//e2是一个指针,存放了一个要比较的元素的地址
//e1指向的元素>e2指向的元素,返回>0的数字
//e1指向的元素==e2指向的元素,返回0
//e1指向的元素<e2指向的元素,返回<0的数字
//void swap(char* buf1, char* buf2, size_t size)
{int i = 0;for (i = 0; i < size; i++){char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}//泛型编程
void bubble_sort(void* base, size_t num, size_t size, int (*cmp)(const void* e1, const void*e2))
{//冒泡排序的趟数int i = 0;for (i = 0; i < num - 1; i++){//一趟冒泡排序int j = 0;for (j = 0; j < num - 1 - i; j++){//if (arr[j] > arr[j + 1])if(cmp((char*)base + j * size, (char*)base + (j + 1) * size)>0){//交换swap((char*)base + j * size, (char*)base + (j + 1) * size, size);}}}
}int cmp_int(const void*e1, const void*e2)
{return *(int*)e1 - *(int*)e2;
}void test1()
{int arr[] = { 0,1,2,3,4,5,6,7,8,9 };//升序//排序为降序int sz = sizeof(arr) / sizeof(arr[0]);print_arr(arr, sz);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);print_arr(arr, sz);
} struct Stu
{char name[20];//20int age;//4
};int cmp_stu_by_age(const void* e1, const void*e2)
{return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}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 arr[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15}};int sz = sizeof(arr) / sizeof(arr[0]);//3bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}int main()
{//整型数据/字符数据/结构体数据...//可以使用qsort函数对数据进行排序//测试bubble_sort,排序整型数据test1();//测试bubble_sort,排序结构体的数据test2();return 0;
这里我们就只需调用函数test2就可以访问了,在test2中我们将所需的参数上传到函数bubble_sort中,这里面我们对两个元素进行比较,调用到函数int cmp_stu_by_age和int cmp_stu_by_name中,在循环里进行比较,传参到函数int cmp_stu_by_age(const void* e1, const voide2)和int cmp_stu_by_name(const void e1, const void* e2)中,如果返回的值大于0就传参到交换函数中进行交换,如果小于0就无需交换,顺着循环和下一个元素进行比较。
我们知道年龄是整形的比较大小非常的简单,那我们的姓名是char型的,那这个怎么比较大小并且排序呢,因为我们的名字是字符串,所以比较名字之间的大小就是比较字符串之间的大小,那么怎么比较字符串的大小呢?那就是字符串对应位置的Ascll值的大小,如果相等的话就跳到下一个字符相比较,以此类推,直到比较出大小为止。
让我们看到下面的代码来验证字符串大小的比较:
#include<stdio.h>
#include<string.h>
int main()
{char arr1[20] = "abc";char arr2[20] = "abe";if (strcmp(arr1, arr2) > 0)printf(">\n");elseprintf("<=\n");return 0;
}
我们看到图片第一个和第二个字符的Ascll值的大小相同,而第三个位置e>c,所以打印的结果就是<=。
那么大家看到都应该更加深刻的了解了qsort函数的实现吧,今天的分享就到这里,谢谢大家。
相关文章:
C语言进阶指针(3) ——qsort的实现
大家好,我们今天来学习回调函数qsort的实现。 首先让我们打开cplusplus.com找到qsort函数。 我们看到这个函数就可以看到它的头文件和参数信息。 #include<stdlib.h> void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const voi…...
Rust源码分析——Rc 和 Weak 源码详解
Rc 和 Weak 源码详解 一个值需要被多个所有者拥有 rust中所有权机制在图这种数据结构中,一个节点可能被多个其它节点所指向。那么如何表示图这种数据结构?在多线程中,多个线程可能会持有同一个数据?如何解决这个问题。 Rc rus…...
【网络编程】深入理解TCP协议二(连接管理机制、WAIT_TIME、滑动窗口、流量控制、拥塞控制)
TCP协议 1.连接管理机制2.再谈WAIT_TIME状态2.1理解WAIT_TIME状态2.2解决TIME_WAIT状态引起的bind失败的方法2.3监听套接字listen第二个参数介绍 3.滑动窗口3.1介绍3.2丢包情况分析 4.流量控制5.拥塞控制5.1介绍5.2慢启动 6.捎带应答、延时应答 1.连接管理机制 正常情况下&…...
社区团购商城小程序v18.1开源独立版+前端
新增后台清理缓存功能 修复定位权限 修复无法删除手机端管理员 11月新登录接口修复! 修复商家付款到零钱, 修复会员登陆不显示头像, 修复无法修改会员开添加绑定...
MATLAB入门-字符串操作
MATLAB入门-字符串操作 注:本篇文章是学习笔记,课程链接是:link MATLAB中的字符串特性: 无论是字符还是字符串,都要使用单引号来‘’表示;在MATLAB中,字符都是在矩阵中存储的,无论…...
Kong Learning
一、Kong Kong是由Mashape公司开源的可扩展的Api GateWay项目。它运行在调用Api之前,以插件的扩展方式为Api提供了管理。比如,鉴权、限流、监控、健康检查等,Kong是基于lua语言、nginx以及openResty开发的,所有拥有动态路由、负载…...
Python怎样写桌面程序
要编写Python桌面应用程序,可以使用以下几种方法: 1.使用Tkinter模块:Tkinter是Python自带的GUI工具包之一,可以使用它来创建基本的GUI界面。例如,可以创建一个简单的窗口,添加按钮、文本框等控件…...
蓝桥杯2023年第十四届省赛真题-平方差--题解
蓝桥杯2023年第十四届省赛真题-平方差 时间限制: 3s 内存限制: 320MB 提交: 2379 解决: 469 题目描述 给定 L, R,问 L ≤ x ≤ R 中有多少个数 x 满足存在整数 y,z 使得 x y2 − z2。 输入格式 输入一行包含两个整数 L, R,用一个空格分隔。 输出格…...
iText实战--根据绝对位置添加内容
3.1 direct content 概念简介 pdf内容的4个层级 层级1:在text和graphics底下,PdfWriter.getDirectContentUnder() 层级2:graphics层,Chunk, Images背景,PdfPCell的边界等 层级3:text层,Chun…...
使用navicat for mongodb连接mongodb
使用navicat for mongodb连接mongodb 安装navicat for mongodb连接mongodb 安装navicat for mongodb 上文mongodb7.0安装全过程详解我们说过,在安装的时候并没有勾选install mongodb compass 我们使用navicat去进行可视化的数据库管理 navicat for mongodb下载地址…...
Qt ffmpeg音视频转换工具
Qt ffmpeg音视频转换工具,QProcess方式调用ffmpeg,对音视频文件进行格式转换,支持常见的音视频格式,主要在于QProcess的输出处理以及转换的文件名和后缀的处理,可以进一步加上音视频剪切合并和音视频文件属性查询修改的…...
机器学习笔记 - 视频分析和人类活动识别技术路线简述
一、理解人类活动识别 首先了解什么是人类活动识别,简而言之,是对某人正在执行的活动/动作进行分类或预测的任务称为活动识别。 我们可能会有一个问题:这与普通的分类任务有什么不同?这里的问题是,在人类活动识别中,您实际上需要一系列数据点来预测正确执行的动作。 看看…...
Redis从入门到精通(三:常用指令)
前边我们介绍了redis存储的四种基本数据类型,并纵向介绍了这四种数据类型的各种指令操作,现在我们这个章节从横向来总结一下关于key的常用指令和数据库常用指令 key常用指令 删除指定key del key 获取key是否存在 exists key 获取key的类型 type …...
代码随想录day39 || 动态规划 || 不同路径
62.不同路径 ● 力扣题目链接 ● 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 ● 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。 ● 问总共有…...
电商平台API接口采集电商平台淘宝天猫京东拼多多数据获取产品详情信息,销量,价格,sku案例
淘宝SKU详情接口是指,获取指定商品的SKU(Stock Keeping Unit,即库存量单位)的详细信息。SKU是指提供不同的商品参数组合的一个机制,通过不同的SKU来标识商品的不同组合形式,如颜色、尺寸等。SKU详情接口可以…...
The ‘<‘ operator is reserved for future use. 错误解决
The < operator is reserved for future use. 错误解决 在 PowerShell 终端执行 python learnstock.py < ldata.txt 发生错误, The < operator is reserved for future use.解决方法, cmd /c python learnstock.py < ldata.txt完结&#x…...
vulnhub靶机Thoth-Tech
下载地址:https://download.vulnhub.com/thothtech/Thoth-Tech.ova 主机发现 arp-scan -l 目标:192.168.21.148 端口扫描 nmap --min-rate 10000 -p- 192.168.21.148 服务扫描 nmap -sV -sT -O -p21,22,80 192.168.21.148 漏洞扫描 nmap --scriptvu…...
不可思议,无密码登录所有网站!
hello,我是小索奇 居然可以免密码登录你的网站?听起来是不是很恐怖 确实如此,Cookie可以用于保持用户在网站上的登录状态,从而实现 免密码登录,学会了不要做坏事哈 这里仅做免密码登录的实操,就不介绍Cooki…...
深度学习编译器关键组件
1 高层中间代码 为了克服传统编译器中采用的IR限制DL模型中复杂计算的表达的局限性,现有的DL编译器利用高层IR(称为图IR)进行高效的代码优化设计。 1.1 图表示 基于DAG的IR:基于DAG的IR是编译器构建计算图的最传统方法之一&…...
【C++】string类模拟实现下篇(附完整源码)
目录 1. resize2. 流插入<<和流提取>>重载2.1 流插入<<重载2.2 流提取 << 3. 常见关系运算符重载4. 赋值重载4.1浅拷贝的默认赋值重载4.2 深拷贝赋值重载实现4.3 赋值重载现代写法 5. 写时拷贝(了解)6.源码6.1 string.h6.2 test.cpp 1. res…...
Android高级开发-APK极致优化
九道工序 1. SVG(Scalable Vector Graphics)可缩放矢量图 使用矢量图代替位图可以减小 APK 的尺寸,因为可以针对不同屏幕密度调整同一文件的大小,而不会降低图像质量。 矢量图首次加载时可能消耗更多的 CPU 资源。之后,二者的内存使用率和…...
Rocketmq--消息驱动
1 MQ简介 1.1 什么是MQ MQ(Message Queue)是一种跨进程的通信机制,用于传递消息。通俗点说,就是一个先进先出的数据结构。 1.2 MQ的应用场景 1.2.1 异步解耦 最常见的一个场景是用户注册后,需要发送注册邮件和短信通…...
华为云云耀云服务器L实例评测|centos系统搭建git私服
搭建git私服 前言一、华为云云耀云服务器L实例租用二、华为云云耀云服务器L实例安装git三、华为云云耀云服务器L实例git配置1.创建文件用于存放公钥2.设置文件权限3.配置本地公钥 四、华为云云耀云服务器L实例部署git仓库四、git仓库到本地总结 前言 之前一直想搭建一个属于自…...
苹果CMS主题 MXonePro二开优化修复开源版影视网站源码
MXPro模板主题(又名:mxonepro)是一款基于苹果cms程序的一款全新的简洁好看UI的影视站模板类似于西瓜视频,不过同对比MxoneV10魔改模板来说功能没有那么多,也没有那么大气,但是比较且可视化功能较多简洁且有周更记录样式等多功能后台设置&…...
【新版】系统架构设计师 - 软件架构设计<轻量级架构>
个人总结,仅供参考,欢迎加好友一起讨论 文章目录 架构 - 软件架构设计<轻量级架构>考点摘要轻量级架构表示层业务逻辑层持久层数据库 SSH与SSMORMHibernate与Mybatis 架构 - 软件架构设计<轻量级架构> 考点…...
系统架构设计专业技能 ·结构化需求分析 - 数据流图
现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。 Now everything is for the future of dream weaving wings, let the dream fly in reality. 点击进入系列文章目录 系统架构设计高级技能 结构化需求分析 - 数据流图 一、数据流图的基本概念二、需…...
linux内核分析:线程和进程创建,内存管理
lec18-19:进程与线程创建 lec20-21虚拟内存管理 内核代码,全局变量这些只有一份,但是内核栈有多份,这可能就是linux线程模型1对1模式的由来。通过栈来做的 x86 CPU支持分段和分页(平坦内存模式)两种 分段,选择子那里就有特权标记了...
SpringMvc根据返回值类型不同处理响应
目录 一、介绍 二、返回值为void (1)控制层方法 三、返回值为String (1)控制层 四、返回值为ModelAndView (1)控制层方法 (2)jsp页面 一、介绍 我们可以通过控制器方法的返回…...
jq命令安装与使用
目录 一、简介二、下载及安装1.Linux 安装2.Windows 安装3.测试安装结果 三、jq用法1.基本语法2.常见用法1)格式化 JSON2)获取属性3)属性不存在情况处理4)数组遍历、截取、展开5)管道、逗号、加号6)数据构造…...
网络面试题汇总
简述 TCP 连接的过程(淘系) 参考答案: TCP 协议通过三次握手建立可靠的点对点连接,具体过程是: 首先服务器进入监听状态,然后即可处理连接 第一次握手:建立连接时,客户端发送 syn 包…...
网站网络资源建立/哪些平台可以发广告
前言: 通过使用一些辅助性工具来找到程序中的瓶颈,然后就可以对瓶颈部分的代码进行优化。一般有两种方案:即优化代码或更改设计方法。我们一般会选择后者,因为不去调用以下代码要比调用一些优化的代码更能提高程序的性能。而一个…...
兰州市做网站建设的公司/大搜推广
1、先按照cocos2d-x的环境配置要求(ant等)安装好,然后在安卓目录下proj.android新建build.bat,脚本如下:echooffecho准备开始echo设置路径setROOT_PATH"F:\test"setPROJECT_PATH"%ROOT_PATH%\proj.android"setRESOURCES_…...
海南工程建设资料备案网站/市场调研表模板
转自:http://www.scrumcn.com/agile/scrum-knowledge-library/scrum.html SCRUM 是一个用于开发和维护复杂产品的框架 Scrum 是一个用于开发和维护复杂产品的框架 ,是一个增量的、迭代的开发过程。在这个框架中,整个开发过程由若干个短的迭代…...
郑州品牌网站建设官网/seo关键词优化培训
近日消息 Windows 10 Build 21343 预览版发布,展现了诸多更新和改善,最重要的是文件管理器的图标得到了更新,一改此前清一色的黄色外观,更加容易分辩。外媒 msftnext 提取了该版本操作系统的图标,并公开了下载链接。这…...
塘厦网站建设公司/做seo排名好的公司
编辑:ll ASEMI代理LSIC1MO120E0080力特车规级MOS管 型号:LSIC1MO120E0080 品牌:LITTELFUSE/力特 封装:TO-247 最大漏源电流:25A 漏源击穿电压:1200V RDS(ON)Max:0…...
网站海外推广/营销案例分析报告模板
域控蓝屏重启,蓝屏代码为:c00002e2蓝屏重启后会自动进入修复模式,在修复模式下选择疑难解答-->高级选项-->启动设置,重启进入安全模式选择界面,选择从 目录修复服务 启动。使用本机计算机管理员账号密码登录服务…...