【c语言——指针详解(4)】
文章目录
- 一、回调函数是什么?
- 二、qsort的使⽤
- 1、使⽤qsort函数排序整型数据
- 2、使⽤qsort排序结构数据
- 三、qsort函数的模拟实现
作者主页
一、回调函数是什么?
回调函数就是⼀个通过函数指针调⽤的函数。
如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数
时,被调⽤的函数就是回调函数。在上一篇文章中,我们实现计算器的第三种代码其实就是运用了回调函数。
//回调函数
int Add(int x, int y)
{return x + y;
}int Sub(int x, int y)
{return x - y;
}int Mul(int x, int y)
{return x * y;
}int Div(int x, int y)
{return x / y;
}void menu()
{printf("************************************\n");printf("******* 1.Add 2.Sub *******\n");printf("******* 3.Mul 4.Div *******\n");printf("******* 0.exit *******\n");printf("************************************\n");
}void Calc(int (*pf)(int, int))
{int x = 0;int y = 0;int ret = 0;printf("请输入2个数\n");scanf("%d %d", &x, &y);ret = pf(x, y);printf("%d\n", ret);
}int main()
{int input = 0;do{menu();printf("请选择:");scanf("%d", &input);switch (input){case 1:Calc(Add);//Add是地址,传递给函数指针Calc,Calc再去调用函数break;case 2:Calc(Sub);break;case 3:Calc(Mul);break;case 4:Calc(Div);break;case 0:printf("退出计算器\n");break;default:printf("选择错误,重新选择\n");break;}} while (input);return 0;
}
相似的代码抽象成函数:有了函数指针后,函数的调用,可以使用函数名来调用,也可以使用函数指针来调用。
二、qsort的使⽤
#include<stdlib.h>//qsort函数的头文件
#include<string.h>//strcmp的头文件
qsort是用来排序的库函数,直接用来排序数据
底层使用的是快速排序的方式,qsort函数可以排序任意类型的数据。
排序方式有很多种,例如:
选择排序
插入排序
冒泡排序
快速排序
希尔排序
…
1、使⽤qsort函数排序整型数据
使用qsort函数前,我们先写一个冒泡排序的函数,对一组整型数据进行排序,排序为升序。
void bubble_sort(int arr[], int sz)
{//趟数int i = 0;for (i = 0; i < sz - 1; i++){//一趟内部的两两比较int j = 0;for (j = 0; j < sz - 1 - i; j++){if (arr[j] > arr[j + 1]){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}}}
}void print_arr(int arr[], int sz)
{int i = 0;for (i = 0; i < sz ; i++){printf("%d ", arr[i]);}printf("\n");
}int main()
{int arr[] = { 9,8,7,6,5,4,3,2,1,0 };//排为升序int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz);print_arr(arr, sz);return 0;
}
像这个函数只能排序整型数据,如果要排序其他类型数据的时候就无能为力了,这时就要用到qsort函数了。
接下来我们看一下qsort函数的原型:
void qsort(void* base, //指针,指向的是待排序的数组的第一个元素size_t num, //是base指向的待排序数组的元素个数size_t size,//base指向的待排序数组的元素的大小int(*compar)(const void*)//函数指针-指向的就是两个元素的比较函数)void*类型的指针是无具体类型的指针,它的作用就是接收任何类型的地址。
这种类型的指针不能直接解引用,也不能+-整数的运算qsort 函数的使用者-明确的知道要排序的是什么数据,这些数据应该如何比较,所以提供两个元素的比较函数
用qsort函数来实现一下冒泡排序:
#include<stdlib.h>//qsort函数的头文件void print_arr(int arr[], int sz)//打印整数
{int i = 0;for (i = 0; i < sz ; i++){printf("%d ", arr[i]);}printf("\n");
}int cmp_int(const void* p1, const void* p2)//比较函数
{/* if (*(int*)p1 > *(int*)p2)//将void*类型的指针p1强制转换为int*类型再解引用,p1和p2就是两个要比较的元素,如9和8{return 1;}else if (*(int*)p1 == *(int*)p2)return 0;elsereturn -1; *///简化:return *(int*)p1 - *(int*)p2;//想要降序就交换这两个数据
}void test1()
{int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_int);//4个参数print_arr(arr, sz);
}int main()
{test1();return 0;
}

2、使⽤qsort排序结构数据
写一段代码私用qsort排序结构体的数据:
struct Stu
{char name[20];int age;
};
这里的两个结构体元素怎么比较大小?
- 按照名字比较-字符串比较–strcmp,strcmp是按照对应着字符串中的字符的ASCII码值比较的
- 按照年龄比较-整型比较
#include<stdlib.h>//qsort函数的头文件
#include<string.h>//strcmp的头文件struct Stu
{char name[20];int age;
};int cmp_stu_by_age(const void* p1, const void* p2) //按年龄排序
{return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age; // 升序 // return ((struct Stu*)p2)->age - ((struct Stu*)p1)->age; // 降序
}int cmp_stu_by_name(const void* p1, const void* p2) //按名字排序
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);//强制类型转换为结构体指针
}void print_stu_arr(struct Stu arr[], int sz) //打印结构体数据
{for (int i = 0; i < sz; i++) {printf("Name: %s, Age: %d\n", arr[i].name, arr[i].age);}
}void test2()
{struct Stu arr[3] = { {"zhangsan", 20}, {"lisi", 35}, {"wangwu", 18} };int sz = sizeof(arr) / sizeof(arr[0]);//qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);//按年龄排序qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);//按名字排序print_stu_arr(arr, sz);
}int main()
{test2();return 0;
}
三、qsort函数的模拟实现
可以使⽤回调函数,来模拟实现qsort(采⽤冒泡的⽅式),bubble—sort来模拟实现qsort,。在这之前我们先看一组代码。
void bubble_sort(int arr[], int sz)
{//趟数int i = 0;for (i = 0; i < sz - 1; i++){//一趟内部的两两比较int j = 0;for (j = 0; j < sz - 1 - i; j++){if (arr[j] > arr[j + 1]){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}}}
}
上述代码可以用来排序整数数据。
在使用冒泡排序的情况下,假设我要改造这个函数,让他能够排序任意类型的数据,哪些地方需要改造呢?我们知道两个整型元素可以直接使用>比较,但是两个字符串,两个结构体元素是不能使用>比较的。
可以把两个元素比较的方法,封装成函数,然后把函数的地址传给排序函数。
#include<stdlib.h>//qsort函数的头文件
#include<string.h>//strcmp的头文件void print_arr(int arr[], int sz)//打印
{int i = 0;for ( i = 0; i < sz; i++) {printf("%d ", arr[i]);}printf("\n");
}int cmp_int(const void* p1, const void* p2) //比较函数
{return (*(int*)p1 - *(int*)p2); // 升序 // return (*(int*)p2 - *(int*)p1); // 降序
}void Swap(char* buf1, char* buf2, size_t width) //这是对地址的四个字节(width)一 一交换从而实现整个交换
{int i = 0;for ( i = 0; i < width; i++) {char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* p1, const void* p2))
{//趟数int i = 0;for (i = 0; i < sz - 1; i++){//一趟内部的两两比较int j = 0;for (j = 0; j < sz - 1 - i; j++){//比较arr[j]和arr[j+1]if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)//改变{//交换两个元素Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}}
}void test3()
{int arr[] = { 3, 1, 7, 8, 5, 3, 4, 9, 0, 6 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);print_arr(arr, sz);
}int main()
{test3();return 0;
}

这段代码实现了一个冒泡排序算法,用于对一个整型数组进行排序,并打印排序后的结果。
- print_arr(int arr[], int sz):这个函数接受一个整型数组arr和数组的大小sz作为参数,并打印数组中的每个元素。
- cmp_int(const void* p1, const void* p2):这是一个比较函数,用于比较两个整型值。它接受两个指向void的指针(任何类型),将它们转换为指向int的指针,并返回两个整数的差值。这个函数的返回值用于确定排序的顺序(升序或降序)。
- Swap(char* buf1, char* buf2, size_t width):这个函数用于交换两个元素的内容。它接受两个指向char的指针(代表元素的起始地址)和一个size_t类型的值width(代表元素的宽度,以字节为单位)。然后,它逐个字节地交换两个元素的内容。注意,对于整型数组来说,这种方法不是最高效的,因为它逐个字节地处理数据,而不是直接交换整个整型值。
- bubble_sort(void* base, size_t sz, size_t width, int (cmp)(const void p1, const void* p2)):这是冒泡排序算法的实现。它接受一个指向数组起始位置的指针base、数组的大小sz、元素的宽度width和一个比较函数cmp作为参数。算法通过多次遍历数组,比较并交换相邻的元素,直到数组完全排序。
同样这个模拟实现的冒泡排序算法也能对结构体元素进行比较:
#include<stdlib.h>//qsort函数的头文件
#include<string.h>//strcmp的头文件struct Stu
{char name[20];int age;
};int cmp_stu_by_age(const void* p1, const void* p2) //按年龄排序
{return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age; // 升序 // return ((struct Stu*)p2)->age - ((struct Stu*)p1)->age; // 降序
}int cmp_stu_by_name(const void* p1, const void* p2) //按名字排序
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);//强制类型转换为结构体指针
}void print_stu_arr(struct Stu arr[], int sz) //打印结构体数据
{for (int i = 0; i < sz; i++){printf("Name: %s, Age: %d\n", arr[i].name, arr[i].age);}
}void Swap(char* buf1, char* buf2, size_t width) //这是对地址的四个字节(width)一 一交换从而实现整个交换
{int i = 0;for ( i = 0; i < width; i++) {char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}void bubble_sort(void* base, size_t sz, size_t width, int (*cmp)(const void* p1, const void* p2))
{//趟数int i = 0;for (i = 0; i < sz - 1; i++){//一趟内部的两两比较int j = 0;for (j = 0; j < sz - 1 - i; j++){//比较arr[j]和arr[j+1]if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)//改变{//交换两个元素Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);}}}
}void test4()
{struct Stu arr[3] = { {"zhangsan",20},{"lisi",35},{"wangwu",18} };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);//bubble_sort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);print_stu_arr(arr, sz);
}int main()
{test4();return 0;
}
按名字排序:

相关文章:
【c语言——指针详解(4)】
文章目录 一、回调函数是什么?二、qsort的使⽤1、使⽤qsort函数排序整型数据2、使⽤qsort排序结构数据 三、qsort函数的模拟实现 作者主页 一、回调函数是什么? 回调函数就是⼀个通过函数指针调⽤的函数。 如果你把函数的指针(地址…...
C# (.net6)实现Redis发布和订阅简单案例
概念: 在 .NET 6 中使用 Redis 的/订发布阅模式。发布/订阅(Pub/Sub)是 Redis 支持的一种消息传递模式,其中一个或多个发布者向一个或多个订阅者发送消息,Redis 客户端可以订阅任意数量的频道。 多个客户端可以订阅一个相同的频道…...
【golang】gorm 使用map实现in 条件查询用法
当 where 字典的值为数组时 gorm 会自动转换为条件 IN 查询 where : map[string]interface{}{} where["id"] [1,2,3] where["name"] "zhangsan"type userList struct {Id int "gorm:id"Name string "gorm:name" } Table.…...
理论篇| 移动端爬虫
移动应用的快速发展和广泛普及带来了海量的数据,这些数据对于市场分析、用户行为洞察和业务优化具有重要价值。然而,由于移动应用的特殊性和防护措施,传统的爬虫技术在采集移动应用数据方面面临许多挑战。因此,App爬虫采集与逆向在爬虫领域的重要性不可低估 然而,App采集…...
systemd实现seatunnel自动化启停
在 systemd 中,您可以通过配置服务单元文件来设置服务在失败或退出后自动重启。这对于确保关键服务在意外退出时能够自动恢复运行非常有用。下面是实现 systemd 自动重启服务的步骤: 通用操作 1. 创建或编辑服务单元文件 假设服务单元文件位于 /etc/systemd/system/my-ser…...
MySQL-08.DDL-表结构操作-创建-案例
一.MySQL创建表的方式 1.首先根据需求文档定义出原型字段,即从需求文档中可以直接设计出来的字段 2.再在原型字段的基础上加上一些基础字段,构成整个表结构的设计 我们采用基于图形化界面的方式来创建表结构 二.案例 原型字段 各字段设计如下&…...
完成Sentinel-Dashboard控制台数据的持久化-同步到Nacos
本次案例采用的是Sentinel1.8.8版本 一、Sentinel源码环境搭建 1、下载Sentinel源码工程 git clone https://github.com/alibaba/Sentinel.git 2、导入到idea 这里可以先运行DashboardApplication.java试一下是否运行成功,若成功,源码环境搭建完毕&a…...
RocketMq详解:三、RocketMq通用生产和消费方法改造
文章目录 1.背景2.通用方法改造2.1添加maven依赖2.2 RocketMq基础配置2.3 配置类2.5 消息传输的对象和结果2.4 消息生产者2.5 消息消费者2.6 功能测试 1.背景 在第二章:《RocketMq详解:二、SpringBoot集成RocketMq》中我们已经实现了消费基本生产和消费…...
基于SpringBoot+Vue+Uniapp的仓库点单小程序的详细设计和实现
2. 详细视频演示 文章底部名片,联系我获取更详细的演示视频 3. 论文参考 4. 项目运行截图 代码运行效果图 代码运行效果图 代码运行效果图 代码运行效果图代码运行效果图 代码运行效果图 5. 技术框架 5.1 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发…...
R语言从多波段tif数据中逐个提取单波段数据
在遥感和地理信息系统(GIS)领域,将多个波段存储在一个文件中可以更有效地进行数据压缩和管理,减少了存储空间的需求。 在R语言中,处理多波段栅格数据通常涉及以下步骤: 读取数据:使用raster包中…...
华为海思:大小海思的双轮驱动战略分析
华为海思,作为华为旗下的半导体设计部门,近年来在芯片设计领域取得了显著成就,成为了中国乃至全球芯片设计的重要力量。实际上,华为海思并非单一实体,而是由两个主要分支构成:大海思和小海思。这两个分支虽然同属华为海思,但在定位、产品布局以及市场策略上有所不同,共…...
LeetCode | 704.二分查找
标准的二分查找,直接上模板! class Solution(object):def search(self, nums, target):""":type nums: List[int]:type target: int:rtype: int"""l 0r len(nums) - 1while l < r:mid (l r 1) / 2if nums[mid] …...
TCP三握四挥
TCP三握(简述) 一开始,客户端和服务端都处于closed状态,服务端主动监听某个端口,处于listen状态 一握要进行C-S的第一个SYN发送,客户端会随机初始化序列号(client_isn)并将其置于TCP首部的序列号字段中,并且将SYN标志…...
java项目之大型商场应急预案管理系统(源码+文档)
项目简介 大型商场应急预案管理系统实现了以下功能: 大型商场应急预案管理系统的主要使用者管理员功能有个人中心,员工管理,预案信息管理,预案类型管理,事件类型管理,预案类型统计管理,事件类…...
【C++】--内存管理
👾个人主页: 起名字真南 👻个人专栏:【数据结构初阶】 【C语言】 【C】 目录 1 C/C内存分布2 C语言中动态内存管理方式 :3 C内存管理方式3.1 new/delete操作内置类型3.2 new和delete操作自定义类型 4 operator new与operator delete4.1 opera…...
【设计模式系列】模板方法模式
一、什么是模板方法模式 模板方法模式(Template Method Pattern)是一种行为型设计模式,它在父类中定义一个算法的框架,允许子类在不改变算法结构的情况下重写算法的某些特定步骤。这种模式非常适合于那些存在共同行为的类&#x…...
java8 Stream流详细API及用法
目录 整理的更全面的API及用法 创建Stream流 中间操作 filter 过滤 map 映射 flatMap 扁平映射 sorted 排序 limit 截断 skip 跳过 distinct 去重 peek 遍历 终端操作 forEach 遍历 forEachOrdered 顺序遍历 min 统计最小值 max 统计最大值 count 统计元素数量 f…...
Redis——持久化
文章目录 Redis持久化Redis的两种持久化的策略定期备份:RDB触发机制rdb的触发时机:手动执行save&bgsave保存测试不手动执行bgsave测试bgsave操作流程测试通过配置,自动生成rdb快照RDB的优缺点 实时备份:AOFAOF是否会影响到red…...
川字结构布局/国字结构布局
1.串字结构布局 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><style&g…...
2013年国赛高教杯数学建模C题古塔的变形解题全过程文档及程序
2013年国赛高教杯数学建模 C题 古塔的变形 由于长时间承受自重、气温、风力等各种作用,偶然还要受地震、飓风的影响,古塔会产生各种变形,诸如倾斜、弯曲、扭曲等。为保护古塔,文物部门需适时对古塔进行观测,了解各种变…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
