深入解析 qsort 函数(下),用冒泡排序模拟实现 qsort 函数

news2024/9/24 17:25:48

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

        本次知识的分享笔者分为上下俩卷文章进行讲解,在本篇文章中,给大家带来 qsort 函数的函数内部构造解析,以及如何通过冒泡排序模拟实现 qsort 的函数功能

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


目录

一.模拟实现函数参数

二.确定算法大体的框架

三.实现函数的判断逻辑、

函数指针部分:

具体判断方法函数:

四.交换函数的实现

代码实现:

五.完整功能测试

六.完整代码


一.模拟实现函数参数

首先我们打开 cplusplus 官网查看 qsort 的参数要求和含义

qsort - C++ Reference (cplusplus.com)

具体参数信息如下 

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

根据上面的定义说明,我们对函数参数进行模拟

void my_qsort(void* base, size_t num, size_t size, int (*compare)(const void* e1, const void* e2))

二.确定算法大体的框架

        正如标题所说,笔者这里使用较为容易理解的冒泡排序,当然其他的快速排序选择排序堆排序希尔排序等算法都是可以使用的,笔者这里只是为了方便讲解具体实现流程和细节,所以选择的冒泡排序的算法

         在选择好了使用什么算法进行模拟实现后,我们就需要来设计大体的框架了,首先,我们列出一般冒泡排序的形式,然后在这基础上进行删改

	for (int i = 0; i < num - 1; i++)
	{
		//每一趟
		for (int j = 0; j < num - i - 1; j++)
		{
			//排序:前一个元素大于后一个就进行交换
			if (arr[j] > arr[j + 1])
			{
				int temp = 0;
				temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}

在这里,我们需要修改的地方主要是俩点

  1. 判断部分,考虑到不同数据的复杂类型,判断部分不能再使用这样简单数组的方式进行判断,应该设计成一个函数指针针对不同的数据类型调用不同的函数来进行不同的判断
  2. 交换部分 ,考虑不同的数据的复杂类型,交换部分也不能使用 int 型的临时变量来进行交换数据,应该设计成对任意数据类型都能交换的方式,也就是对字节直接进行操作对于不同的数据类型,我们直接对其内存存储的字节进行交换,这样就能达到目的和要求

三.实现函数的判断逻辑、

函数指针部分:

        首先,为了实现不同的判断方法,我们要有一个函数指针去调用不同的判断方法,我们根据 qsort 的参数要求设计如下函数指针

int (*compare)(const void* e1, const void* e2)

        这个函数指针有俩个参数,分别代表着需要判断排序的数据,我们调用的参数的返回值是有要求的,如下图 

那我们就可以将这个判断逻辑的函数的返回值来作为 if 语句判断依据,实现实例如下

if (compare((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
{

}

        这代表着,如果判断函数返回值大于0,那我们就执行交换操作,如果等于0或者小于0就不执行交换操作,那我们实现了函数调用的接口,我们还得具体的实现判断逻辑函数,方便进行调用

具体判断方法函数:

        根据我们在上卷文章中讲解的判断逻辑和实现方法,我们可以同样试用在这里,具体细节如有不懂可以访问下面的链接

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

//比较函数--整形
int comp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}

//比较函数--浮点型
int comp_float(const void* a, const void* b)
{
	return *(float*)b - *(float*)a;
}

//比较函数--按照年龄
int comp_age(const void* a, const void* b)
{
	return ((struct stu*)b)->age - ((struct stu*)a)->age;
}

//比较函数--按照姓名
int comp_name(const void* a, const void* b)
{
	return strcmp(((struct stu*)a)->name, ((struct stu*)b)->name);
}

四.交换函数的实现

        首先,我们要确定我们要进行交换的对象,对于冒泡排序来说,需要交换的只是前后俩个元素,那我们拿到他们的地址就可以进行交换了,我们具体实现如下

//交换
swap((char*)base + j * size, (char*)base + (j + 1) * size, size);

这里分别对三个参数进行一下解释:

  1. (char*)base + j * size :确定要交换的第一个数据的地址
  2. (char*)base + (j + 1) * size :确定要交换的第二个数据的地址
  3. size :确定这俩个数据直接隔了多少个字节,方便逐字节进行操作

        对于参数为什么这样设定,有小伙伴可能不理解,下面笔者就以 int 类型举例,我们知道,int 类型占 4 个字节,而 char* 指针解引用可以访问一个字节,而数据在内存中存储的最小字节都是 1 个字节那我们就可以 通过 char* 指针来逐个访问字节,就像下面这张图一样,我们逐个交换俩个数据中的每一个字节,在交换完成后,我们就相当于把这俩个数字进行了交换 

 

        在逐个交换字节后,宏观上给我们的感受就是直接交换了我们的数据内容,也就是说我们使用俩个 char* 指针分别访问要交换的俩个在数据,每一次交换一个字节后,指针加一,我们就可以继续访问交换后面的字节,在这里循环往复交换 size 个字节后,这俩个数据就被我们完全交换了

 

代码实现:

        注意,在具体写代码的过程中不要非法访问空间,在下方代码的注释部分,笔者对错误代码进行了注释,大家在写过程中一定得注意,在指针访问一些只读区域或者不是我们申请的空间的区域就会出现段错误就像注释部分的代码,我们定义一个空指针,然后再对这个空指针进行解引用访问赋值的操作就会出现报错

//交换函数
void swap(char* buf1, char* buf2, size_t size)
{
	//逐个字节进行交换,一共有size个字节
	for (int i = 0; i < size; i++)
	{
		//错误示范
		//char* temp = 0;
		//*temp = *buf1;
		//*buf1 = *buf2;
		//*buf2 = *temp;
		//buf1++;
		//buf2++;
		
		//正确示范
		char temp = *buf1;
		*buf1 = *buf2;
		*buf2 = temp;
		buf1++;
		buf2++;
	}
}

五.完整功能测试

        我们仍然使用在上篇文章中的用例进行测试,观察我们设计的函数能不能完成我们预期的功能,在这里对整形数组,浮点型数组,结构体数组进行测试

int main()
{
	int arr1[] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr1) / sizeof(arr1[0]);
	print1(arr1);
	my_qsort(arr1, sz, sizeof(int), comp_int);
	print1(arr1);

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


	struct stu arr3[] = { {"zhangsan",19},{"lisi",20},{"wangswu",21} };
	my_qsort(arr3, 3, sizeof(arr3[0]), comp_name);
	my_qsort(arr3, 3, sizeof(arr3[0]), comp_age);

	system("pause");

	return 0;
}

观察结果:

        我们可以发现和我们预期的一样,对于不同数据类型,我们自己模拟的函数都可以实现排序功能 

六.完整代码

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<windows.h>

//结构体
struct stu
{
	char name[20];
	int age;
};

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

void print2(float* arr)
{
	for (int i = 0; i < 5; i++)
	{
		printf("%.2f ", *(arr + i));
	}
	printf("\n");
}

//比较函数--整形
int comp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}

//比较函数--浮点型
int comp_float(const void* a, const void* b)
{
	return *(float*)b - *(float*)a;
}

//比较函数--按照年龄
int comp_age(const void* a, const void* b)
{
	return ((struct stu*)b)->age - ((struct stu*)a)->age;
}

//比较函数--按照姓名
int comp_name(const void* a, const void* b)
{
	return strcmp(((struct stu*)a)->name, ((struct stu*)b)->name);
}

//交换函数
void swap(char* buf1, char* buf2, size_t size)
{
	//逐个字节进行交换,一共有size个字节
	for (int i = 0; i < size; i++)
	{
		//错误示范
		//char* temp = 0;
		//*temp = *buf1;
		//*buf1 = *buf2;
		//*buf2 = *temp;
		//buf1++;
		//buf2++;
		
		//正确示范
		char temp = *buf1;
		*buf1 = *buf2;
		*buf2 = temp;
		buf1++;
		buf2++;
	}
}

//void qsort(void* base, size_t num, size_t size,int (*compar)(const void*, const void*));
void my_qsort(void* base, size_t num, size_t size, int (*compare)(const void* e1, const void* e2))
{
	//使用冒泡排序
	for (int i = 0; i < num - 1; i++)
	{
		//每一趟
		for (int j = 0; j < num - i - 1; j++)
		{
			//排序:前一个元素大于后一个
			if (compare((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				//交换
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}

int main() 
{
	int arr1[] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr1) / sizeof(arr1[0]);
	print1(arr1);
	my_qsort(arr1, sz, sizeof(int), comp_int);
	print1(arr1);

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


	struct stu arr3[] = { {"zhangsan",19},{"lisi",20},{"wangswu",21} };
	my_qsort(arr3, 3, sizeof(arr3[0]), comp_name);
	my_qsort(arr3, 3, sizeof(arr3[0]), comp_age);

	system("pause");

	return 0;
}

        

        本次分享的俩篇文章就到此为止啦,希望我的分享对您有所帮助,文章中如有错误,欢迎积极指正,您的认可就是我最大的动力,那我们下次分享再见

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

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

相关文章

LeetCode:两数之和

题目描述&#xff1a; 这是一道用暴力解法&#xff0c;逻辑十分简单、清晰的一道题&#xff0c;直接遍历数target-num[i]就行 而官方给了第二种巧妙的解法&#xff1a;运用哈希表。此法可将时间复杂度从O&#xff08;N^2&#xff09;降到O&#xff08;1&#xff09; 其思路是…

rhel8防火墙firewalld操作

1.查看默认区域 [rootlocalhost r]# firewall-cmd --get-default-zone public2.查看网卡关联的区域 [rootlocalhost r]# firewall-cmd --get-zone-of-interfaceifcfg-ens160 external 3.设置网卡的默认区域修改为work [rootlocalhost r]# firewall-cmd --zonework --change…

综合管廊安全监测,助力市政管廊智能化管理

综合管廊是一种集管线维护、建设、管理于一体的地下综合通道&#xff0c;可以将电力、通讯、燃气、供热、供水等工程管线集于一体&#xff0c;综合管廊对于城市建设具有重要意义&#xff0c;可以防止管线破裂&#xff0c;杜绝反复开挖路面&#xff0c;有效缓解交通拥堵&#xf…

ISAC通信感知一体化学习记录

文章目录 写在前面Fundamental Limits for ISAC: Information and Communication Theoretic PerspectiveIntroductionperformance metricsCommunication and Estimation Rates PHY Tradeoff and Resource Allocation for ISACbackgroundThe Related Works Preliminaries of the…

JDK9特性——概述

文章目录 引言JDK9特性概述JDK9的改变JDK和JRE目录变化总结 引言 JAVA8 及之前&#xff0c;版本都是特性驱动的版本更新&#xff0c;有重大的特性产生&#xff0c;然后进行更新。 JAVA9开始&#xff0c;JDK开始以时间为驱动进行更新&#xff0c;以半年为周期&#xff0c;到时…

淘宝问大家怎么投诉不良评价?

大花客服外包 商家朋友们都知道&#xff0c;正向的“问大家”可以很大程度提高转化率&#xff0c;负面的会对转化率有很不好的影响。那当遇到“问大家”中存在不良内容时&#xff0c;该如何投诉呢&#xff1f; 一、手机淘宝APP举报问大家不良内容 【问大家的提问】举报受理范围…

Makefile基础

迷途小书童 读完需要 4分钟 速读仅需 2 分钟 1 引言 下面这个 C 语言的代码非常简单 #include <stdio.h>int main() {printf("Hello World!.\n");return 0; } 在 Linux 下面&#xff0c;我们使用下面的命令编译就可以 gcc hello.c -o hello 但是随着项目的变大…

FPGA projet : VGA

在vga屏幕上显示 &#xff1a; 野火科技 相比于上个工程&#xff0c;只需要修改 vga_pix 模块即可。 注意存储器类型变量的定义&#xff1a;reg 【宽度】<名称>【深度】 赋值 always &#xff08;poseedge vga_clk&#xff09;begin 为每一行赋值&#xff0c;不可位赋…

淘宝直播流量底层逻辑规则

大花客服外包 位商家朋友们在运营店铺的过程中&#xff0c;是不是最担心的就是没有流量&#xff1f;今天针对直播间流量分配规则底层原理&#xff0c;给大家做一个深度剖析。 一、十大直播间流量来源 十大直播间流量来源公域流量私域流量 1、封面图点击率&#xff08;公域&am…

Django系列:Django应用(app)的创建与配置

Django系列 Django应用&#xff08;app&#xff09;的创建与配置 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article…

【7z密码】7z压缩包密码忘记了,怎么办?i

7z压缩包设置了密码&#xff0c;解压的时候就需要输入正确对密码才能顺利解压出文件&#xff0c;正常当我们解压文件或者删除密码的时候&#xff0c;虽然方法多&#xff0c;但是都需要输入正确的密码才能完成。忘记密码就无法进行操作。 那么&#xff0c;忘记了7z压缩包的密码…

广义IoU

使用相对误差相似可能导致模型在回归大小边界框时表现不一致的原因是&#xff0c;边界框的尺度不同会对相对误差产生不同的影响。 假设我们有两个边界框&#xff0c;一个较小&#xff0c;一个较大&#xff0c;它们的真实尺寸分别为10x10和100x100。如果我们的模型在回归这两个边…

神兵利器 - sigurls侦察工具,Squeak 通过.net执行shellcode,脚本自动爬取所有接口swagger-hack

神兵利器 - sigurls侦察工具&#xff0c;Squeak 通过.net执行shellcode&#xff0c;脚本自动爬取所有接口swagger-hack。 ############################# 免责声明&#xff1a;工具本身并无好坏&#xff0c;希望大家以遵守《网络安全法》相关法律为前提来使用该工具&#xff0…

MATLAB中scatter3函数用法

目录 语法 说明 向量和矩阵数据 表数据 其他选项 示例 创建三维散点图 改变标记大小 改变标记颜色 填充标记 设置标记类型 设置标记属性 绘制表中的数据 使用自定义标记大小和颜色绘制表数据 指定三维散点图的坐标区 使用句柄设置散点序列属性 scatter3函数的功…

打开深度学习的锁:(1)入门神经网络

打开深度学习的锁 导言PS&#xff1a;神经网络的训练过程一、导入的包和说明二、数据的预处理2.1 数据集说明2.2 数据集降维度并且转置2.3 数据预处理完整代码 三、逻辑回归3.1 线性回归函数公式3.2 sigmoid函数公式 四、初始化函数五、构建逻辑回归的前向传播和后向传播5.1 损…

FDM3D打印系列——Blue Mary

大家好&#xff0c;我是阿赵。   这次打印一个拳皇里面的Blue Mary。   打印这个模型的原因&#xff0c;是看到有网友说这个模型用FDM打印不出来&#xff0c;有些人评论说要光固化才行。所以我也想试试。结果是成功的。 一、打印过程 这个模型是分为了5个部分&#xff0c…

PowerShell脚本免杀/bypass/绕过杀毒软件,ReconFTW 漏洞扫描

PowerShell脚本免杀/bypass/绕过杀毒软件&#xff0c;ReconFTW 漏洞扫描。 #################### 免责声明&#xff1a;工具本身并无好坏&#xff0c;希望大家以遵守《网络安全法》相关法律为前提来使用该工具&#xff0c;支持研究学习&#xff0c;切勿用于非法犯罪活动&#…

Observability:使用 OpenTelemetry 手动检测 Go 应用程序

作者&#xff1a;Luca Wintergerst DevOps 和 SRE 团队正在改变软件开发的流程。 DevOps 工程师专注于高效的软件应用程序和服务交付&#xff0c;而 SRE 团队是确保可靠性、可扩展性和性能的关键。 这些团队必须依赖全栈可观察性解决方案&#xff0c;使他们能够管理和监控系统&…

Git配置SSH

前言&#xff1a; Git是分布式的代码管理工具&#xff0c;远程的代码管理是基于SSH的&#xff0c;所以要使用远程的Git则需要SSH的配置 温馨提示&#xff1a; 1.查看是否已经有了ssh公钥&#xff1a;cd ~/.ssh 如果没有则不会有此文件夹&#xff0c;有则删除 一、git 配置 &a…

性能测试之性能监控和性能优化

目录 一.概述二.jconsole和jvisualvm三.jconsole四.visualvm1.输入命令提示 jvisualvm不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件2.打开jvisualvm里面不包含GC监控 五.性能优化1.优化中间件&#xff08;中间件越多&#xff0c;系统性能越差&#xff09;2.优…