【C语言进阶篇】回调函数都学了吧!那么用冒泡排序实现qsort函数你会嘛?


在这里插入图片描述

🎬 鸽芷咕:个人主页

 🔥 个人专栏:《C语言初阶篇》 《C语言进阶篇》

⛺️生活的理想,就是为了理想的生活!

文章目录

  • 📋 前言
  • 💬 qsort 和 冒泡排序的区别
        • 📑 qsort 的特点
        • 📑 冒泡排序 的特点
    • 💭 如何解决只能排序整形
        • 📖(void *)指针讲解
        • 📖(void* )类型的指针该如何使用
      • ✅ 解决方法
    • 💭 如何解决只能排序整形
      • 📖 冒泡排序需要改进的地方
        • ✅ 改进方法
        • ✅ 参数讲解
    • 💭 如何解决不同类型的交换问题
        • ✅ swap交换函数的实现
  • 💬 bubble_sort实现完全体
    • 💭 bubble_sort完整代码
        • 🌈 测试排序整形数组
        • 🌈 测试排序结构体
  • 📝全篇总结

📋 前言

  🌈hello! 各位宝子们大家好啊,前面一章讲解了qsor快排函数的使用那么我们是否可以自己实现一下他呢?
  ⛳️冒泡排序我们都知道只能排序整形,但是回调函数学完了之后就可以完美解决这个问题,下面就来看看吧!
  📚本期文章收录在《C语言进阶篇》,大家有兴趣可以看看呐
  ⛺️ 欢迎铁汁们 ✔️ 点赞 👍 收藏 ⭐留言 📝!

🔥 注:VS2022 等C语言学习工具都在《学习工具专栏》, 还有各种实用调试技巧有兴趣可以去看看呐!

💬 qsort 和 冒泡排序的区别

📑 qsort 的特点

🔥 注:快排函数qsort的使用博主在《qsort的使用详解》详细讲解过哦,不会可以去看看。

qsort的特点是:

  • 可以排序任意类型的数据
  • 使用快速排序的思想 quick

📑 冒泡排序 的特点

冒泡排序 的特点:

  • 只能排序整形数据

冒泡排序 思想:

  • 俩俩相邻的元素进行比较,满足条件就交换

好了这俩种排序的思想和区别我们都明白了!冒泡排序我相信大家都不陌生,那么我们今天的任务就是使用冒泡排序的思想去模拟实现库函数qsort 函数的功能!

  • 而这需要解决冒泡排序3个缺陷
  • 一、只能排序整形
  • 二、不同类型的数据比较方法不一样
  • 三、不同类型数据如何交换方法也不一样

💭 如何解决只能排序整形

这个是冒泡排序最主要的问题,那么改如何解决呢?既然是模拟实现qsort 函数那么我们就可以借鉴一下 qsort 函数的方法!

  • qsort 函数里面直接用 通用类型指针 接收的数据
  • 通用类型指针 是不是刚好能解决冒泡排序只能接收整数的问题
    在这里插入图片描述

📖(void *)指针讲解

void我们都知道是一个空类型的意思,void 就是无类型的指针 :*

  • 无具体类型的指针,可以说他为通用类型指针
  • 但是这种类型的指针是不能够直接进行解引用操作的
  • 由于类型是空类型所以也不能进行指针运算
  • 因为既然他是个空类型那么我们 + - 是该跳过多少字节呢?

📚示例一:
在这里插入图片描述

  ⛳️这里就就可以看出一旦指针类型不同是不可以接收不同类型的地址的!

  • 而用 void* 类型的指针就不会出现这种情况

📚示例二:
在这里插入图片描述

📖(void* )类型的指针该如何使用

  ⛳️前面说了这种指针既不能直接解引用,又不能进行指针运算那么我们该怎么使用void*类型的指针呢?

  • 🌱 其实void*类型的指针在使用的时候需要强制转换一下就好了!
  • 🌱 这样这个空指针类型不就有类型了(我们强制转换的类型)
  • 🌱 那么指针的运算不也解决了?因为有类型了就可以知道
  • 🌱 加一步我们可以跳过多少字节

📑图片展示:
在这里插入图片描述

✅ 解决方法

现在我们知道了 qsort 快排函数的参数 和 通用类型指针 void* 如何使用那么解决冒泡排序只能排序整形还不简单嘛?

  • 既然是模拟实现 qsort 那么就先仿着 qsort 的参数写
  • 来实现我们的冒泡排序 bubble_sort
    在这里插入图片描述

📚 代码演示:

//模拟实现 qsort 
void bubble_sort(void* base, //第一个参数的地址size_t num,//要比较元素的个数size_t size, //比较元素的大小int (*cmp)(const void* , const void*) )//比较函数的地址

这里我们就把要模拟实现的函数 bubble_sort 的参数给写好了,由于我们也要排序不同类型的参数所以,肯定是需要元素类型大小

  • 从哪里排序的第一个参数地址
  • 以及要排序多少个元素
  • 和每个元素怎么进行比较

💭 如何解决只能排序整形

大家都知道冒泡排序在比较整数的时候字需要简单的进行比个大小就好了。但是我们这里需要对不同类型进行比较就不能进行以前那种简单的比较方法了!

  • 那么该怎么解决呢?这个其实也很简单 qsort
  • 库函数里面需要我们自己写一个比较函数来进行判断如何比较
  • 那么我们也可以使用这种方法,对于不同的数据由使用者来决定如何比较
  • 我们只需要调用就好了。

📖 冒泡排序需要改进的地方

在这里插入图片描述

✅ 改进方法

📚 代码演示:

这里我们可以怎么改进呢?前面说了对于不同的数据由使用者来决定如何比较!我们只需要写一个回调函数就好了!

  • 使用回调函数就可以在这里解决问题
  • 一个函数可以调用多种不同的函数

🔥 注: 回调函数的详细讲解和使用实例在这里《回调函数的实战技巧》

void bubble_sort(void* base, size_t num, size_t size, int (*cmp)(const void* , const void*) )
{int i = 0;for (i = 0; i < num - 1; i++){int j = 0;for (j = 0; j < num - 1 - i; j++){if ( cmp((char*)base+j*size, (char*)base +( j +1)* size)>0){int tmp = arr[i];arr[i] = arr[i + 1];arr[i + 1] = tmp;}}}
}

✅ 参数讲解

cmp((char*)base+jsize, (char)base +( j +1)* size)>0
这个函数调用是如何写出来的呢? 虽然我们的比较函数是由使用者来实现的!但是我们只是可以调用函数,而函数的参数还是需要我们在 bubble_sort 里面传出去的。

  • 既然要比较就需要 第一个 第二个 俩个相邻的元素
  • void* 类型的指针又不能直接使用,我们还要排序不同类型的元素所以类型转换就不能写死
  • 把它强转为 (char*) 是最合理的,一个字节!

而我们又知道每个元素的类型大小是多少,这不就和巧妙嘛!(char*)base+j*size char* 指针是每次访问一个字节,那么乘上我们的元素类型大小就刚好可以访问不同类型的元素!

  • 假设我们参数是整形数组
  • 那么 (char*)base+j*size 就是访问4个字节(char*)base+j*4
  • 刚好是一个整形的大小。

💭 如何解决不同类型的交换问题

而冒泡排序以前的交换算法也肯定不可取了,这就需要我们自己构建一种可以交换任意类型的数据了!
在这里插入图片描述

✅ swap交换函数的实现

既然是交换那么肯定需要俩个参数,所以 (char*)base+j*size(char*)base +( j +1)* size) 这俩个参数肯定是需要的

  • 而我们传过来的参数是强转成 char* 的
  • 那么我们是不是可以这样交换
    在这里插入图片描述

一个字节一个字节的进行交换,诶是不是很神奇。这样是不是也能把俩个元素个相互交换并且内容都不发生改变。

  • 因为交换的本质还是一样,只不是以前是一步完成的!
  • 我们现在交换了4次,只是次数变多了但结果是一样的!
  • 都是把不同字节的内容给交换了!
  • 只需要知道要交换元素类型大小是多少,所以我们还需要一个类型大小 size 传过来!

📚 代码演示:

//交换函数
void swap(char* p1, char* p2,int size)
{int i = 0;for (i = 0; i < size; i++){char tmp = *p1;*p1 = *p2;*p2 = tmp;p1++;p2++;}
}

💬 bubble_sort实现完全体

好了这下我们冒泡排序的所有缺点都解决了,折现就可以验证一下 bubble_sort 冒泡排序模拟实现的 qsort 在功能上是不是一样的!

💭 bubble_sort完整代码

📚 代码演示:

//交换函数
void swap(char* p1, char* p2,int size)
{int i = 0;for (i = 0; i < size; i++){char tmp = *p1;*p1 = *p2;*p2 = tmp;p1++;p2++;}
}//测试 bubble_sort 整数排序
//void qsort(void* base, size_t num, size_t size,//int (*compar)(const void*, const void*))
void bubble_sort(void* base, size_t num, size_t size, int (*cmp)(const void* , const void*) )
{int i = 0;for (i = 0; i < num - 1; i++){int j = 0;for (j = 0; j < num - 1 - i; j++){if ( cmp((char*)base+j*size, (char*)base +( j +1)* size)>0){swap((char*)base + j * size, (char*)base + (j + 1) * size,size);}}}
}

🌈 测试排序整形数组

📚 代码演示:

#include <stdio.h>
#include <string.h>//交换函数
void swap(char* p1, char* p2,int size)
{int i = 0;for (i = 0; i < size; i++){char tmp = *p1;*p1 = *p2;*p2 = tmp;p1++;p2++;}
}//测试 bubble_sort 整数排序
//void qsort(void* base, size_t num, size_t size,//int (*compar)(const void*, const void*))
void bubble_sort(void* base, size_t num, size_t size, int (*cmp)(const void* , const void*) )
{int i = 0;for (i = 0; i < num - 1; i++){int j = 0;for (j = 0; j < num - 1 - i; j++){if ( cmp((char*)base+j*size, (char*)base +( j +1)* size)>0){swap((char*)base + j * size, (char*)base + (j + 1) * size,size);}}}
}
//整形比较函数
int int_cmp(const void* p1, const void* p2)
{return (*(int*)p1) - (*(int*)p2);
}test1()
{int arr[10] = { 1,9,5,3,4,2,10,8,7,6 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), int_cmp);//打印函数}int main()
{test1();return 0;
}

🌈 测试排序结构体

📚 代码演示:

#include <stdio.h>
#include <string.h>struct stu
{char name[20];int		age;
};//交换函数
void swap(char* p1, char* p2,int size)
{int i = 0;for (i = 0; i < size; i++){char tmp = *p1;*p1 = *p2;*p2 = tmp;p1++;p2++;}
}//测试 bubble_sort 整数排序
//void qsort(void* base, size_t num, size_t size,//int (*compar)(const void*, const void*))
void bubble_sort(void* base, size_t num, size_t size, int (*cmp)(const void* , const void*) )
{int i = 0;for (i = 0; i < num - 1; i++){int j = 0;for (j = 0; j < num - 1 - i; j++){if ( cmp((char*)base+j*size, (char*)base +( j +1)* size)>0){swap((char*)base + j * size, (char*)base + (j + 1) * size,size);}}}
}//结构体比较函数
int struct_cmp(const void* p1, const void* p2)
{return strcmp( ((struct stu*)p1)->name,((struct stu*)p2)->name);
}//测试排序结构体
void test2()
{struct stu arr[3] = { {"zhangsan",20},{"lisi",40},{"wangwu",33} };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), struct_cmp);
}
int main()
{test2();return 0;
}

📝全篇总结

✅ 归纳:
好了以上就是关于模拟实现qsort 函数的全部讲解了,学会这些你就可以完美的使用回调函数!
  qsort函数和冒泡排序的区别
  只能排序整形的改进方法
  判断部分的改进
  交换函数的实现
  测试数据
☁️ 以上就全部内容了,本章是对回调函数的应用和提高大家学会了嘛!
看到这里了还不给博主扣个:
⛳️ 点赞☀️收藏 ⭐️ 关注

💛 💙 💜 ❤️ 💚💓 💗 💕 💞 💘 💖
拜托拜托这个真的很重要!
你们的点赞就是博主更新最大的动力!
有问题可以评论或者私信呢秒回哦。
在这里插入图片描述

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

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

相关文章

JS逆向之猿人学爬虫第20题-wasm

文章目录 题目地址sign参数分析python算法还原往期逆向文章推荐题目地址 https://match.yuanrenxue.cn/match/20第20题被置顶到了第1页,题目难度 写的是中等 算法很简单,就一个标准的md5算法,主要是盐值不确定, 而盐值就在wasm里面,可以说难点就在于wasm分析 sign参数分…

[Linux]进程间通信

[Linux]进程间通信 文章目录 [Linux]进程间通信进程间通信什么是进程间通信进程间通信的目的进程间通信的本质为什么存在进程间通信进程间通信的分类 管道什么是管道匿名管道本质pipepipe的使用匿名管道读写情况匿名管道的特征 命名管道本质命令行创建命名管道创建和删除命名管…

如何在电脑上查看连接过的wifi信息?

忘记wifi密码&#xff1f;想要看看wifi信息&#xff1f; 我想这篇文章可以帮到你O(∩_∩)O哈哈~。 通过网络连接中心查看 电脑上找到“网络和共享中心” 点击连接的wifi名称 点击无线属性 在安全选项中就有密码 通过电脑命令行工具查看推荐 通过winr快捷键打开电脑运…

手动搭建gateway,项目集成gateway实现Token效果

目录 背景步骤1、首先创建springboot项目2、引入依赖3、配置文件&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff08;超级重要&#xff01;&#xff01;&#xff01;根据自己的需要进行配置&#xff09;4、相关类我们在服务中进行的白名单中接口的操作如…

信驰达推出RTL8720DN系列2.4G和5G双频Wi-Fi+蓝牙二合一模块

近日&#xff0c;领先的无线物联网通信模块厂商深圳信驰达科技RF-star推出了基于RTL8720DN SoC的2.4 GHz和5 GHz双频Wi-Fi蓝牙二合一模块—RF-WM-20DNB1。 图 1信驰达RF-WM-20DNB1 Wi-Fi模块 RF-WM-20DNB1是一款低功耗单芯片无线蓝牙和Wi-Fi组合模块&#xff0c;支持双频(2.4 G…

详细介绍如何使用HuggingFace和PyTorch进行医学图像分割-附源码

医学图像分割是一种创新过程,使外科医生能够拥有虚拟的“X 射线视觉”。它是医疗保健领域非常有价值的工具,可提供非侵入性诊断和深入分析。考虑到这一点,在这篇文章中,我们将探索威斯康辛大学麦迪逊分校胃肠道图像分割Kaggle 挑战数据集。作为该项目的一部分,我们将使用 …

爬虫005_python类型转换_其他类型转换为整型_转换为Float类型_转换为字符串_转换为布尔值---python工作笔记023

首先来看,字符串转换成int 很简单 float转换成int 会把小数点后面的内容丢掉 boolean转换为int true是1 false 是0 然后字符串转换为int,要注意 不能有特殊字符比如1.23 中有点 就报错 上面字符串12ab,有ab也报错 看上面

Docker可视化管理工具Portainer多机器安装使用

一、首先得安装docker Docker安装并指定主目录:https://blog.csdn.net/wdy_2099/article/details/77367107 二、使用docker方式安装portainer 安装命令如下&#xff1a; docker run -it -d \-p 8999:9000 \--name portainer \--restart always \-v /var/run/docker.sock:/v…

Linux Ubuntu crontab 添加错误 提示:no crontab for root - using an empty one 888

资料 错误提示&#xff1a; no crontab for root - using an empty one 888 原因剖析&#xff1a; 第一次使用crontab -e 命令时会让我们选择编辑器&#xff0c;很多人会不小心选择默认的nano&#xff08;不好用&#xff09;&#xff0c;或则提示no crontab for root - usin…

【图像分类】CNN + Transformer 结合系列.1

介绍三篇结合使用CNNTransformer进行学习的论文&#xff1a;CvT&#xff08;ICCV2021&#xff09;&#xff0c;Mobile-Former&#xff08;CVPR2022&#xff09;&#xff0c;SegNetr&#xff08;arXiv2307&#xff09;. CvT: Introducing Convolutions to Vision Transformers, …

Windows bat 查找文件被哪个进程占用,并终止该进程

一、背景 我有个批处理脚本如下&#xff1a; echo off chcp 936 & cls cd /D F:\Chen\python3\ExciseC set fdate%date:~0,4%%date:~5,2%%date:~8,2% python main.py >> crawl_record_%fdate%.log 2>&1 for /F %%f in (dir crawl_record_*.log /B ^| find /…

机器学习——异常检测

异常点检测(Outlier detection)&#xff0c;⼜称为离群点检测&#xff0c;是找出与预期对象的⾏为差异较⼤的对象的⼀个检测过程。这些被检测出的对象被称为异常点或者离群点。异常点&#xff08;outlier&#xff09;是⼀个数据对象&#xff0c;它明显不同于其他的数据对象。异…

Linux工具【1】(编辑器vim、编译器gcc与g++)

vim详解 引言vimVim的三种模式及模式切换普通模式下操作底行模式下操作 gcc与ggcc的使用&#xff08;g类似&#xff09;预编译编译汇编链接静态库与动态库 总结 引言 vim&#xff08;vi improved&#xff09;编辑器是从 vi 发展出来的一个文本编辑器。 代码补全、编译及错误跳…

opencv python 训练自己的分类器

源码下载 一、分类器制作 1.样本准备 收集好你所需的正样本&#xff0c;和负样本&#xff0c;分别保存在不同文件夹 在pycharm新建项目&#xff0c;项目结构如下&#xff1a;has_mask文件夹放置正样本&#xff0c;no_mask文件夹放置负样本 安装opencv&#xff0c;把opencv包…

Day.4 刷题练习(自守数)

题目&#xff1a; 例子&#xff1a; 分析题目&#xff1a; 主要目的&#xff1a;给定一个范围小于等于N&#xff0c;在这个范围中找自守数&#xff08;自身等于平方后的尾部数据如5&#xff1a;5 ^ 2 25 &#xff0c; 然后 5 与 平方的后的尾部相等&#xff09; 思路&#x…

ancos注册中心、网关和静态化freemarker、对象存储服务MinIO

1、docker安装ancos ①&#xff1a;docker拉取镜像 docker pull nacos/nacos-server:1.2.0②&#xff1a;创建容器 docker run --env MODEstandalone --name nacos --restartalways -d -p 8848:8848 nacos/nacos-server:1.2.0③&#xff1a;访问地址&#xff1a;http://192…

Qt : day4

1.思维导图 2.服务器 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);//给服务器指针实例化空间server new QTcpServer(this);}Widget::~Widget() {delete ui;…

下级平台级联视频汇聚融合平台EasyCVR,层级显示不正确的原因排查

视频汇聚平台安防监控EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有GB28181、RTSP/Onvif、RTMP等&#xff0c;以及厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等&#xff0c;能对外分发RTSP、RTMP、FLV、HLS、WebRTC等…

【计算机网络】第 3 课 - 计算机网络体系结构

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01; 时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、常见的计算机网络体系结构 2、计算机网络体系结构分层的必要性 2.1、物理层 2.2、数据链路层 2.3、网路层 2.4、运输层 2…

Sentinel nacos spring cloud 持久化配置---分布式/微服务流量控制

文章目录 sentinel控制台安装目标实现代码地址版本说明maven spring-cloud-starter-alibaba-sentinel依赖yml文件Nacos业务规则配置看源码配置规则SentinelProperties 总配置加载DataSourcePropertiesConfiguration 配置标准的nacos配置注册具体sentinel配置 外传 sentinel控制…