指针之旅(5)—— 万能指针与回调函数的搭配:万能排序qsort函数的使用规则及其模拟实现。

news2024/9/20 18:48:28

目录

1. 回顾:万能指针void* 与 回调函数 的特性

1.1 万能指针void*

1.2 回调函数

2. qsort函数的使用规则

2.1 qsort的头文件和排序方向

2.2 qsort的函数参数表解析

2.3 结构体数组排序举例

3. 冒泡排序模拟万能排序qsort的实现

3.1 冒泡排序的回顾与疑问

3.2 qsort的模拟实现(万能版冒泡排序)


1. 回顾:万能指针void* 与 回调函数 的特性

1.1 万能指针void*

在《指针之旅(2)——const修饰词 && 野指针、空指针与泛型指针》中我们就学习过void*型指针了,本篇我们需要用到它的3个性质:

  1. void*指针可以⽤来接收任意类型地址。(这也是它被称作万能指针的原因)
  2. void* 指针有些指针运算是不能直接进行:(1)指针的+-整数(2)指针解引⽤。 
  3. 如果非要用void*指针进行上述运算,必须对void*指针进行强制类型转换。(指针类型决定访问步长,void*指针的步长为0,不能运算)

1.2 回调函数

在《指针之旅(4)—— 指针与函数:函数指针、转移表、回调函数》中也学习过回调函数了,这里再简单介绍一下回调函数:

  1. 回调函数回调触发函数(主调函数)中的一个形式参数。
  2. 回调触发函数(主调函数)用一个函数指针来接收回调函数的地址,方便主调函数调用回调函数。(该函数指针的去*类型是跟回调函数的类型是一致的)
  3. 主调函数的函数体中,会使用该函数指针来调用回调函数,具体要实现什么功能由回调函数的内容决定

2. qsort函数的使用规则

2.1 qsort的头文件和排序方向

使用qsort函数要包含头文件<stdlib.h>

qsort默认的排序规则是"从小到大排序(升序排序)",这与回调函数的规则有关。

2.2 qsort的函数参数表解析

qsort的函数原型:

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

qsort与其他排序函数一样,无返回值。下面介绍一下这4个参数:

1. 参数base:指的是待排序数组的首地址

  • 作用:这个参数告诉qsort函数需要排序的数据从何处开始。

2. 参数num:表示的是数组中元素的个数

  • 作用:这个参数确保qsort函数知道排序操作需要处理多少个数据项。

3. 参数size:是数组中每个元素的大小(类型),以字节为单位。

  • 作用:这个参数至关重要,因为它告诉qsort函数每个数组元素占据多少内存空间。

4. 参数compar:compar是指向比较函数的函数指针

该比较函数使用者实现,有以下默认的限定

  1. 如果 元素1 大于 元素2,则返回大于0的值。
  2. 如果 元素1 等于 元素2,则返回0。
  3. 如果 元素1 小于 元素2,则返回小于0的值。

qsort只规定这3条默认限定,不做其他规定。如果限定1和限定3的内容刚好相反,那么qsort由升序排序 变为 降序排序

举例说明:用qsort函数对一个整型数组排序。

首先我们要自己写出一个整型数据的比较函数,针对上述3项限定,我们可以写出这两个比较函数:

int int_cmp1(const void* p1, const void* p2)
{
	if (*(int*)p1 > *(int*)p2)
		return 1;
	else if (*(int*)p1 < *(int*)p2)
		return -1;
	else
		return 0;
}

int int_cmp2(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2 );
}

疑点解惑:

1. 应该用哪个比较函数?

答:这两个比较函数都是正确的,因为它们都符合qsort的3条默认限定。

2. 为什么比较函数中的参数类型是const void*?

答:首先,qsort是个万能排序函数,void*可以接收任意类型的数据。

其次,去掉void*类型来看,const修饰的对象是*p1和*p2,也就是说*p1和*p2的值不能被改变,这保证了数组的元素不会在比较函数中不会被指针改变。

3. 作比较时,为什么使用的是*(int*)p1,而不是*p1?

答:因为p1是void*型的指针,该类型的指针访问的步长为0,即不能访问。要先进行强制类型转换,才有访问的步长(权限)。

现在我们有了自己的比较函数,可以对整型数组排序了:

2.3 结构体数组排序举例

现在知道怎么使用qsort了,让我们尝试用qsort对一个“学生信息”结构体数组进行排序:

结构体的定义:

struct Stu         //“学生信息”结构体
{
    char name[20];        //名字
    int age;                   //年龄
};

1.按名字升序排序:

//strcmp - 是库函数,是专⻔⽤来⽐较两个字符串的⼤⼩的
int cmp_stu_name(const void* e1, const void* e2)
{
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
//按照名字来排序
void test1()
{
	struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
	int sz = sizeof(s) / sizeof(s[0]);
	qsort(s, sz, sizeof(s[0]), cmp_stu_name);
}

2.按年龄升序排序:

int cmp_stu_age(const void* e1, const void* e2)
{
	return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
//按照年龄来排序
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_age);
}

3. 冒泡排序模拟万能排序qsort的实现

库函数中的万能排序函数是由快速排序算法quick_sort演化而来的,鉴于大多数人没学过快速排序,所以这里我用冒泡排序算法来演示。

3.1 冒泡排序的回顾与疑问

我们先来回顾一下冒泡排序怎么写:

void bubble_sort(int arr[], int sz)
{
	for (int i = 0; i < sz-1; i++)	//i是趟数,要排sz-1趟
	{
		for (int j = 1; j < sz-i; j++) //arr[sz-i]以及它后面的元素已经排好顺序
		{
			if (arr[j - 1] > arr[j])	//整型数据的比较
			{
				int t = arr[j];			//整型数据的交换
				arr[j] = arr[j - 1];
				arr[j - 1] = t;
			}
		}
	}
}

在冒泡排序的框架下,对于其他类型的数组来说:还是要排sz-1趟,排序中的arr[sz-i]以及它后面的元素已经排好顺序。所以 i 和 j 的for循环大体不用改变。

但是原本bubble_sort的比较部分和数据交换部分都是只针对int型的。而万能排序是能对所有类型都能排序的(比如正数、浮点数、字符、字符串),所以这一部分要改。

3.2 qsort的模拟实现(万能版冒泡排序)

我们仿照库函数qsort的参数表写出了冒泡排序版的万能排序函数bubsort:

void void_swap(const void* e1, const void* e2, size_t size)
{
	for (int n = 0; n < size; n++)
	{
		char t = *((char*)e1+n);        重点
		*((char*)e1+n) = *((char*)e2+n);
		*((char*)e2 + n) = t;
	}
}

void bubsort(void* base, size_t num, size_t size, int (*compar)(const void* e1, const void* e2))
{
	for (int i = 0; i < num-1; i++)
	{
		for (int j = 1; j < num-i; j++)
		{
			//如果arr[j-1] > arr[j]就交换
			if (compar((char*)base + (j - 1) * size, (char*)base + j * size) > 0)   重点
				void_swap((char*)base+(j-1)*size, (char*)base+j*size, size);	重点
		}
	}
}

这里有几个重点需要说明一下:

1. 怎样得到数组arr[j-1]与arr[j]的地址来比较大小?

很简单,对base加够整数使得该结果能指向元素arr[j-1]与arr[j]的首地址就行。

需要注意的是:base现在是void*指针,需要强制类型转换;我们有参数size,代表着数组每个元素的大小,单位是一个字节。即base+j*size就是数组arr[j]的地址,所以base要强制类型转换成char*型。

2. 怎样交换arr[j-1]和arr[j]的数据内容?

首先要知道一点:数据是一个字节一个字节来存储的,如果一个字节一个字节地交换数据,交换到该元素的最后一个字节,那么整个元素就完成了交换。

所以在这里我们交换size次,每次只交换char型大小的数据。

现在我们来试试这个万能冒泡排序bubsort能不能使用:

int int_comp(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}
//用bubsort排序整型数组:
int main()
{
	int arr[] = { 1,3,4235,353,54,53,323,4,2,42,7,5 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubsort(arr, sz, sizeof(arr[0]), int_comp); 

	for (int i = 0; i < sz; i++)
		printf("%d ", arr[i]);
	return 0;
}

结果:

我们成功了!!!


本期分享结束,感谢大家的支持Thanks♪(・ω・)ノ

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

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

相关文章

前端自定义下载文件名

data数据格式如下 "data": [{"createBy": "system","createTime": "2024-09-11 14:08:56","updateBy": "","updateTime": null,"beginTime": null,"endTime": null,&qu…

18068 选择排序

### 思路 1. **初始化**&#xff1a;定义变量i, j, k和临时变量tmp。 2. **外层循环**&#xff1a;遍历数组的每个元素&#xff0c;i从0到n-2。 3. **内层循环**&#xff1a;从i1到n-1&#xff0c;找到最小元素的索引k。 4. **交换**&#xff1a;将最小元素与当前元素交换。 #…

源码安装python3.10.8后pip3无法使用问题

一、背景&#xff1a; CentOS7.7上默认已经存在python2.7&#xff0c;但需要python3&#xff0c;所以计划源码安装python3。 下载python3.10.8 wget https://www.python.org/ftp/python/3.10.8/Python-3.10.8.tgz 二、编译安装 安装用户为普通linux用户&#xff0c;拥有sudo权…

实战案例(2)防火墙+二交换机VLAN组网

案例二&#xff1a;防火墙充当三层交换机与路由器角色功能进行组网 拿到这样的拓扑后&#xff0c;首先要了解好客户的需求&#xff0c;然后根据需求进行划分 比如客户那边有监控跟办公网络&#xff0c;可以通过VLAN划分不同的区域&#xff0c;然后二层交换机对接终端的口划入到…

C++中的I/O流

本节主要看代码理解 I/O流继承关系 iostream 主要类 cin cout cerr clog while&#xff08;cin>>str&#xff09; &#xff5b; //处理 &#xff5d; 当接收ctrl z 或 ctrl c时&#xff0c;会停止&#xff0c; 原理&#xff1a;重载操作符bool&#xff0c;令指定istr…

python学习——对无人机影像有RGB转换到HSV

问题描述 最近需要对无人机影像中绿色植被信息进行提取&#xff0c;查看相关论文&#xff0c;发现用的比较多的就是HSV色彩转换方法&#xff0c;动手实践一下。 解决思路 #mermaid-svg-5ejGodIusPv6zFVS {font-family:"trebuchet ms",verdana,arial,sans-serif;fon…

移植案例与原理 - startup子系统之syspara_lite系统属性部件

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 startup子系统之syspara_lite系统属性部件 &#xff08;1&#xff09; startup子系统之syspara_lite系统属性部件 &#xff08;2&#xff09; startup子系…

【C++ Primer Plus习题】14.5

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream> using namespace std; #includ…

Xinstall地推解决方案:精准追踪,提升App推广效果!

在移动互联网时代&#xff0c;App的推广和运营成为了开发者们面临的一大挑战。尤其是地推活动&#xff0c;作为App推广的重要手段&#xff0c;其效果直接关系到产品的用户增长和活跃度。然而&#xff0c;传统的地推方式存在着诸多痛点&#xff0c;如业绩统计繁琐、推广效果难以…

Excel怎么去除公式保留数字,一个快捷键也能搞定

大家好&#xff0c;这里是效率办公指南&#xff01; &#x1f4ca;在处理Excel数据时&#xff0c;我们经常会遇到需要从公式单元格中提取数值的情况。例如&#xff0c;你可能有一个包含公式的列&#xff0c;但只需要那些公式计算后的数字。今天&#xff0c;我们就来学习几种在…

无线领夹麦克风哪款好,无线麦克风品牌排行榜前十名,智商税详谈

​无线领夹麦克风如今是视频创作者、直播博主等群体常用的音频设备&#xff0c;但由于品牌和型号众多&#xff0c;消费者在购买时常常不知如何选择。从市场情况来看&#xff0c;某些品牌在销量上占据优势&#xff0c;但在口碑方面&#xff0c;一些专注品质的小众品牌可能更胜一…

《DB-GPT项目》专栏总目录

❤️ 专栏名称&#xff1a;《DB-GPT项目》 &#x1f339; 内容介绍&#xff1a;项目部署、大模型替换、底层源码修改、数据分析、数据可视化、自动化等&#xff0c;适合零基础和进阶的同学。 &#x1f680; 订阅专栏&#xff1a;订阅后可阅读专栏内所有内容&#xff0c;专栏持续…

配电房数字式仪表读数识别算法开发

文章目录 一、概述二、训练数据准备2.1 自动生成图片2.2 爬虫搜集图片 三、模型训练及测试3.1 数据集组成3.2 模型训练及评价3.3 预测结果可视化 四、小结 一、概述 数字式仪表是指以数字的形式呈现仪表读数的仪表类型&#xff0c;其特点是读数比较直观&#xff0c;如下图为配…

react 安装使用 antd+国际化+定制化主题+样式兼容

安装antd 现在从 yarn 或 npm 或 pnpm 安装并引入 antd。 yarn add antd修改 src/App.js&#xff0c;引入 antd 的按钮组件。 import React from react; import { Button } from antd;const App: React.FC () > (<div className"App"><Button type&q…

【鸿蒙】HarmonyOS NEXT星河入门到实战5-基础语法

目录 一、字符串拼接 1.1 常规字符串拼接 1.2 模板字符串hello(符号在键盘的tab上面) 二、类型转换 &#xff08;数字和字符串&#xff09; 2.1 字符串转数字 2.2 数字转字符串 三、交互 3.1 点击事件 3.2 状态管理 3.3 计数器案例 四、运算符 4.1 算数运算符 4.2 赋…

[001-03-007].第26节:分布式锁迭代3->优化基于setnx命令实现的分布式锁-防锁的误删

我的博客大纲 我的后端学习大纲 1、问题分析&#xff1a; 1.1.问题&#xff1a; 1.锁的超时释放&#xff0c;可能会释放其他服务器的锁 1.2.场景&#xff1a; 1.如果业务逻辑的执行时间是7s。执行流程如下 1.index1业务逻辑没执行完&#xff0c;3秒后锁被自动释放。2.index…

企业会议室预约管理系统

基于springbootvuemysql实现的企业会议室预约管理系统&#xff08;源码数据库部署视频&#xff09; ### 主要技术 SpringBoot、Vue、MySQL ### 系统角色 员工、管理员 ### 系统功能 1&#xff09;管理员&#xff1a;数据统计&#xff08;会议室使用统计-柱状图、设备状态统计…

C++:2024/9/11

B. Increase/Decrease/Copy 原题链接&#xff1a;Problem - B - Codeforces 题目大意&#xff1a; ​ 给一颗树&#xff0c;规定编号为1的节点为根节点&#xff0c;每个节点上初始会有一个值&#xff0c;你每次可以进行操作&#xff0c;这个操作是选定一个非叶子节点的节点…

Parallels Desktop 20 最新版,带来哪些新功能(附下载链接)!

很兴奋地向大家宣布&#xff0c;Parallels Desktop 20 for Mac 正式发布啦——这是我们产品迄今为止最强大的版本&#xff01; 这次最大的亮点是全新推出的 Parallels AI 工具包&#xff0c;它提供安全的、可下载的预装虚拟机&#xff0c;让你可以在离线环境中迅速提升 AI 开发…

python中swift包的安装

魔搭社区上经常会有swift这种包需要导,但是在pip install swift怎么装都装不上,这时候需要: pip install ms-swift -U