C语言 指针(4) qsort函数

目录

前言

一、回调函数

二、qsort函数

2.1 使用qsort函数排序整型数据

2.2 使用qsort排序结构数据

三、qsort函数的模拟实现

总结


前言

今天我们主要来学习一下C语言中的qsort排序函数。


一、回调函数

回调函数就是⼀个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被用来调用其所指向的函数
时,被调用的函数就是回调函数。回调函数不是由该函数的实现方直接调用,⽽是在特定的事件或
件发生时由另外的⼀⽅调⽤的,用于对该事件或条件进⾏响应。
我们之前写的计算器程序,也可以用回调函数来完成:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int add(int a, int b)
{return a + b;
}
int sub(int a, int b)
{return a - b;
}
int mul(int a, int b)
{return a * b;
}
int div(int a, int b)
{return a / b;
}
void num(){printf("*************************\n");printf(" *****1:add 2:sub********\n");printf(" *****3:mul 4:div********\n");printf(" *****0:exit     ********\n");printf("*************************\n");
}
int col(int(*pf)(int, int)) {int x, y = 0;scanf("%d%d", &x, &y);int z = pf(x, y);return z;
}
int main() {int input = 1;int (*pf[5])(int, int) = { 0,add,sub,mul,div };do{num();printf("请选择:");scanf("%d", &input);int z = 0;switch (input) {case 1:z=col(add);printf("%d\n", z);break;case 2:z=col(sub);printf("%d\n", z);break;case 3:z=col(mul);printf("%d\n", z);break;case 4:z=col(div);printf("%d\n", z);break;case 0:printf("退出程序\n");break;default:printf("输入错误\n");}} while (input);return 0;
}int main() {int arr[10] = { 1,2,3,4,5,1,2,3,4,6 };int sz = sizeof(arr) / sizeof(arr[0]);int i,j= 0;for (i = 0; i < sz; i++) {int count = 0;for (j = 0; j < sz; j++) {if (arr[i] == arr[j])count++;}if (count == 1)printf("%d ", arr[i]);}}

二、qsort函数

qsort函数定义

对数组的元素进行排序

对数组中按base数指向的 num 个元素进行排序,每个元素size字节长,使用 compar 函数确定顺序。

语法形式:

void qsort (void* base,指针,指向待排数组的第一个元素 size_t num, //是base指向待排数组的元素个数size_t size,//是base指向待排数组的元素大小int (*compar)(const void*,const void*));//函数指针  指向两个元素比较的函数

其中compar函数的返回值有三种情况:

如果前一个元素比后一个元素大,返回值大于零,相等返回值等于零,小返回值小于零。

2.1 使用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;
}

其中int_cmp就是 compar 函数来确定两个元素的大小

int int_cmp(const void * p1, const void * p2)
{return (*( int *)p1 - *(int *) p2);//需要强制类型转换为int *
}

2.2 使用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函数
我们知道qsort函数有四个参数,那么我们定义的函数应该也要有四个参数:
bubb_comp_qsort(arr, sz, width, int_cmp);     

和qsort类似,分别是第一个元素地址,数组大小,数组每个元素大小,元素比较函数。

首先是主函数:

int main() {int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };size_t sz = sizeof(arr) / sizeof(arr[0]);size_t width = sizeof(arr[0]);bubb_comp_qsort(arr, sz, width, int_cmp);int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}
}

其中qsort函数可以接受不同类型的数据,所以我们冒泡函数的判断和交换语句就要发生改变:

void bubb_comp_qsort(void* bash, size_t sz, size_t width, int(*cmp)(void*, void*)) {int i = 0;for (i = 0; i < sz - 1; i++) {int j = 0;for (j = 0; j < sz - 1 - i; j++) {if (cmp((char*)bash + j * width, (char*)bash + (j + 1) * width) > 0){_swap((char*)bash + j * width, (char*)bash + (j + 1) * width, width);}}}}

首先是比较函数cmp

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

这里需要两个地址来当函数参数,我们给bash进行强制类型转换为char*(更好的计算,如果转换为int*指针,加1跳过四个字节,如果元素大小为7个字节,那么不会加到7)

然后在加上j乘以每个元素的大小就是元素的地址来,后一个元素地址就是j+1;

接下来如果条件判定成功,就交换函数了

void _swap(void* p1, void* p2, size_t width) {int i = 0;for (i = 0; i < width; i++) {char tmp = *((char*)p1 + i);*((char*)p1 + i) = *((char*)p2 + i);*((char*)p2 + i) = tmp;}
}

其中需要的也是两个元素的地址,同时也要知道元素的大小(宽度),不然不知道交换到哪里。

我们采用一个字节一个字节交换,进行循环,循环次数就是元素的大小。

完整代码:

#include <stdio.h>int int_cmp(const void* p1, const void* p2)
{return (*(int*)p1 - *(int*)p2);
}
void _swap(void* p1, void* p2, size_t width) {int i = 0;for (i = 0; i < width; i++) {char tmp = *((char*)p1 + i);*((char*)p1 + i) = *((char*)p2 + i);*((char*)p2 + i) = tmp;}
}void bubb_comp_qsort(void* bash, size_t sz, size_t width, int(*cmp)(void*, void*)) {int i = 0;for (i = 0; i < sz - 1; i++) {int j = 0;for (j = 0; j < sz - 1 - i; j++) {if (cmp((char*)bash + j * width, (char*)bash + (j + 1) * width) > 0){_swap((char*)bash + j * width, (char*)bash + (j + 1) * width, width);}}}}
int main() {int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };size_t sz = sizeof(arr) / sizeof(arr[0]);size_t width = sizeof(arr[0]);bubb_comp_qsort(arr, sz, width, int_cmp);int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}
}


总结

上述文章我们继续深入了解指针,同时讲了qsort函数的运用和实现。希望对你有所帮助。

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

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

相关文章

spring启动时如何自定义日志实现

一、现象 最近在编写传统的springmvc项目时&#xff0c;遇到了一个问题&#xff1a;虽然在项目的web.xml中指定了log4j的日志启动监听器Log4jServletContextListener&#xff0c;且开启了日志写入文件&#xff0c;但是日志文件中只记录业务代码中我们声明了日志记录器的日志&a…

力扣98、530、501-java刷题笔记

一、98. 验证二叉搜索树 - 力扣&#xff08;LeetCode&#xff09; 1.1题目 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左 子树 只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点…

[leetcode~dfs]1261. 在受污染的二叉树中查找元素

给出一个满足下述规则的二叉树&#xff1a; root.val 0 如果 treeNode.val x 且 treeNode.left ! null&#xff0c;那么 treeNode.left.val 2 * x 1 如果 treeNode.val x 且 treeNode.right ! null&#xff0c;那么 treeNode.right.val 2 * x 2 现在这个二叉树受到「污…

C#四部曲(知识补充)

Unity跨平台原理 .Net相关 只要编写的时候遵循.NET的这些规则&#xff0c;就能在.NET平台下通用 各种源码→根据.NET规范编写→(虚拟机)生成CIL中间码(保存在程序集中)→转成操作系统原代码 跨语言← 跨平台↓ Unity跨平台原理&#xff08;Mono&#xff09; c#脚本→MonoC#编…

MySQL 事务的原理以及长事务的预防和处置

transaction_isolation 隔离级别 读未提交 读提交 视图是在每个 SQL 语句开始执行的时候创建的 可重复读 视图是在事务启动时创建的&#xff0c;整个事务存在期间都用这个视图 串行化…

安装OneNote for Win10 | Win10/Win11

前言 PC端的OneNote分为2个版本&#xff0c;分别是Microsoft Store版本和Office版本&#xff0c;Microsoft Store版本即为OneNote for Win10&#xff0c;此版的OneNote有最近笔记功能&#xff0c;但检索功能不如Office版本&#xff0c;个人认为2个版本各有优劣。 但OneNote f…

【硬件基础】电容的选型

1、电容的理论基础 电容器的本质就是储能&#xff0c;充放电 根据作用可分为&#xff1a;滤波电容&#xff0c;旁路电容&#xff0c;耦合电容&#xff0c;退耦电容&#xff0c;自举电容 2、电容的取值 计算取值&#xff0c;查手册&#xff0c;经验取值 3、电容的选取 分为铝…

“删边“的并查集------反向并查集

目录 1.题目2.思路3.代码 默认大家都会并查集了 1.题目 小美认为&#xff0c;在人际交往中&#xff0c;但是随着时间的流逝&#xff0c;朋友的关系也是会慢慢变淡的&#xff0c;最终朋友关系就淡忘了。 现在初始有一些朋友关系&#xff0c;存在一些事件会导致两个人淡忘了他们…

AssetBundle打包与加载

官方文档 参照视频 1.AssetBundle打包 1.1设置资源的命名和后缀 命名只支持小写 1.2创建Editor文件夹&#xff0c;在里面创建编辑器打包AssetBundle的脚本 using UnityEditor; using System.IO;public class CreateAssetBundles {[MenuItem("Assets/Build AssetBun…

旅游景区公共广播 园区广播 公路服务区广播

旅游景区公共广播 园区广播 公路服务区广播 旅游景区公共广播 旅游景区公共广播(又称背景音乐)简称BGM&#xff0c;它的主要作用是掩盖噪声并创造一种轻松和谐的气氛&#xff0c;是一种创造轻松愉快环境气氛的音乐。掩盖环境噪声&#xff0c;创造与旅游景区相适应的气氛&#…

遥感云计算的一个拐点

GeoForge&#xff0c;一个值得关注的遥感大数据应用 简介 GeoForge是由Ageospatial公司开发的一个基于大语言模型(GeoLLMs)的地理空间分析平台。GeoForg的目的是使每个人都可以轻松进行地图绘制和地理空间分析&#xff0c;无论您是外行还是专家。 Geo for ChatGPT 作者团队已…

07-java基础-锁之AQSReentrantLockBlockingQueueCountDownLatchSemapho

文章目录 0&#xff1a;AQS简介-常见面试题AQS具备特性state表示资源的可用状态AQS定义两种资源共享方式AQS定义两种队列自定义同步器实现时主要实现以下几种方法&#xff1a;同步等待队列条件等待队列 1&#xff1a;AQS应用之ReentrantLockReentrantLock如何实现synchronized不…

双环PID控制详细讲解

参考博客&#xff1a; &#xff08;1&#xff09;PID双环控制&#xff08;速度环和位置环&#xff09; &#xff08;2&#xff09;PID控制&#xff08;四&#xff09;&#xff08;单环与双环PID&#xff09; &#xff08;3&#xff09;内外双环pid算法 0 单环PID 目标位置→系…

阿里云第一次面试记录

java多态&#xff1f; 多态表示一个对象具有多种的状态&#xff0c;具体表现为父类的引用指向子类的实例 Fu f Zi z(); 多态是同一个行为具有多个不同表现形式或形态的能力。 多态就是同一个接口&#xff0c;使用不同的实例而执行不同操作 特点&#xff1a; 对象类型和引用类型…

Opencv 插值方法 总结

一、概括 面试的时候问到了一个图&#xff0c;就是如何将一个算子放缩&#xff1f;&#xff1f;我第一反应是resize&#xff08;&#xff09;,但是后来我转念一想&#xff0c;人家问的是插值方式&#xff0c;今天来总结一下 最邻近插值法原理分析及c实现_最临近插值法-CSDN博…

【数据库】Oracle内存结构与参数调优

Oracle内存结构与参数调优 Oracle 内存结构概览oracle参数配置概览重要参数&#xff08;系统运行前配置&#xff09;:次要参数&#xff08;可在系统运行后再优化调整&#xff09;: Oracle数据库服务器参数如何调整OLTP内存分配操作系统核心参数配置Disabling ASMM&#xff08;禁…

【图文详解】Maven Helper插件解决Maven冲突

文章目录 插件问题解决过程 在面试中解决问题的能力和思路是考察的重点&#xff0c;面试官问会问我们有没有解决过maven冲突。以下造了一个maven冲突&#xff0c;手把手教学如何解决Maven冲突。 插件 插件在idea插件中搜索Maven Helper 问题 解决过程 根据上面日志知道是log…

让生活更加精致的APP?

晚上好&#xff0c;今天博主来介绍几款帮助你条理生活的APP&#xff0c;让你的生活更加精致&#xff0c;充满仪式感。 一&#xff0e;格志日记 一款以“格子”的方式记录日记的APP&#xff0c;非常简单明了&#xff0c;用户可以依据自己的喜好&#xff0c;来自由定义或者删除格…

力扣刷题Days16(js)-67二进制求和

目录 1,题目 2&#xff0c;代码 2.1转换进制数 2.2模拟加法 3&#xff0c;学习与总结 Math.floor() 模拟加法思路回顾 重点复习巩固 模拟加法的思路和学习位运算&#xff1b; 今天没精力了&#xff0c;先休息 1,题目 给你两个二进制字符串 a 和 b &#xff0c;以二进制…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的手写数字和符号识别(深度学习训练+UI界面+训练数据集)

摘要&#xff1a;开发手写数字和符号识别对于智能交互系统具有关键作用。本篇博客详细介绍了如何运用深度学习构建一个手写数字和符号识别&#xff0c;并提供了完整的实现代码。该系统基于强大的YOLOv8算法&#xff0c;并对比了YOLOv7、YOLOv6、YOLOv5&#xff0c;展示了不同模…