【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题 古塔的变形 由于长时间承受自重、气温、风力等各种作用,偶然还要受地震、飓风的影响,古塔会产生各种变形,诸如倾斜、弯曲、扭曲等。为保护古塔,文物部门需适时对古塔进行观测,了解各种变…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
深度学习水论文:mamba+图像增强
🧀当前视觉领域对高效长序列建模需求激增,对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模,以及动态计算优势,在图像质量提升和细节恢复方面有难以替代的作用。 🧀因此短时间内,就有不…...
通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器
拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件: 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...
