【指针的进阶(3)】回调函数和qsort排序各种类型的数据

news2024/12/26 11:05:11

文章目录

  • 前言
  • 一、回调函数是什么?
    • 如何实现回调函数
  • 二、回调函数的应用——qsort
    • qsort排序各种类型的数据
  • 总结


前言

前两章讲了指针的类型,数组传参和指针传参,还有函数指针和函数指针数组,接下来第三章讲回调函数


指针函数非常大的用途就是实现回调函数

一、回调函数是什么?

回调函数就是通过函数指针调用的函数。

如果把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

如何实现回调函数

下面的代码过于冗余
如果分装一个函数,调用它,能大大减少了敲代码工作量,这个函数就是回调函数
在这里插入图片描述
所以分装一个函数叫Calc,在使用加减乘除时调用这个函数

        case 1:
			Calc(Add);
			break;
		case 2:
			Calc(Sub);
			break;
		case 3:
			Calc(Mul);
			break;
		case 4:
			Calc(Div);
			break;

函数地址传给Calc函数,用函数指针接收
这个pf是函数指针,指向的参数是(int, int),返回类型是int

void Calc(int (*pf)(int, int))

具体代码如下

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("请输入两个操作数:");
	scanf("%d %d", &x, &y);
	ret = pf(x, y);
	printf("ret = %d\n", ret);
}
int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			Calc(Add);
			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;
}

过程如下:
在这里插入图片描述

不是直接调用Add函数,而是把Add函数传给pf指针函数,,通过pf函数指针去调用Add函数,实现计算,那么pf函数指针就是回调函数 。

作用: 回调函数更具有广泛性和通用性,代码不易写死,如果直接调用Add函数,代码就被固定住了,要想调用别的函数,那个代码就不适用了。

二、回调函数的应用——qsort

qsort 是标准库里的函数,用来排序

qsort函数怎么实现回调函数呢?

这就得说到冒泡排序了
一般冒泡排序是这样写的:

int main()
{
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	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;
			}
		}
	}
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

这如果要排结构体,浮点型等其他类型的数据呢,这个代码就存在一定的问题了,只适用于整型的排序,形式固定住了,不灵活不广泛。

那有没有一种写一个排序函数,适用于任何类型呢?
qsort函数就能解决这个问题

qsort排序各种类型的数据

qsort函数的特点
1.快速排序的方法
2.适合于任意类型数据的排序

  1. 在cplusplus网站上搜索 qsort,可以看到qsort有四个参数:

在这里插入图片描述
四个函数的意思是:
void qsort(
void* base,//指向需要排序的数组的第一个元素
size_t num,//排序的元素个数
size_t size,//一个元素的大小,单位是字节
int(*cmp)(const void*, const void*)
);//函数指针类型-这个函数指针指向的函数,能够比较base指向数组中的两个元素


                      用qsort排序整型

根据上面四个参数,写出下面这段代码
在这里插入图片描述

  1. 最后一个参数空出来没有写,是因为这里涉及到一些重要的知识点
    第四个参数是要写一个函数,能够比较base指向数组中的两个元素,并把结果返回。 那么就写一个函数叫cmp_int,参数就是cplusplus网站上qsort函数给的两个参数在这里插入图片描述
    代码如下:
int cmp_int(const void* p1, const void* p2)
  1. 写完了函数,怎么比较两个数呢?
    在cplusplus网站上找到计算方法如下:
    在这里插入图片描述
    当p1指向的值小于p2指向的值时,返回小于0的数字;
    当p1指向的值等于p2指向的值时,返回0;
    当p1指向的值大于p2指向的值时,返回大于0的数字;

那么就让p1p2两个数作差,把结果返回去。

但这里还涉及到一些知识:
一个热知识::void* 的指针是无具体类型的指针。
void* 类型的指针可以接收任意类型的地址

在函数参数这里用void* 的好处就是,广泛性,什么类型的数据排序都能接收,编程不会报警告。
但这种类型的指针不能直接解引用,也不能直接进行指针运算。所以在比较两个数大小时,需要强制类型转换。
意思就是要排序什么类型,就强制类型转换什么类型
比如:当要比较整型数据时,p1和p2要强制类型转换成整型,当比较浮点型数据时,p1和p2强制类型转换成浮点型。

代码如下:

int cmp_int(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}
  1. 最后把结果打印出来
    分装一个print函数
    参数是整型数组和数组大小
    循环打印每个元素
void print(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

完整代码如下:

#include<stdlib.h>
int cmp_int(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}
void print(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
test1()
{
	int arr[10] = {9,8,7,6,5,4,3,2,1,0};
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr,sz,sizeof(arr[0]),cmp_int);
	print(arr, sz);
}
int main()
{
	test1();
	return 0;
}

注意:qsort 函数的头文件是 #include<stdlib.h>


                     用qsort 排序结构体

年龄比较大小

#include<stdio.h>
#include<stdlib.h>
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;
}

void print(struct Stu arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i].age);
	}
}
void test2()
{
	struct Stu arr[] = { {"zhangsan",20},{"lisi",50},{"wangwu",15} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
	print(arr,sz);
}
int main()
{
	test2();
	return 0;
}

姓名比较大小

#include<stdio.h>
#include<stdlib.h>
struct Stu

{
	char name[20];
	int age;
};

int cmp_str_stu_by_name(const void* p1, const void* p2)

{
	return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}
//名字比较不能相减,名字是字符串,字符串比较大小用strcmp

void print(struct Stu arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%s ", arr[i].name);
	}
}
void test3()

{
	struct Stu arr[] = { {"zhangsan",50},{"lisi",15},{"wangwu",30} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_str_stu_by_name);
	print(arr, sz);
}

总结

本章讲了回调函数的含义如何实现回调函数qsort排序各种类型的数据的内容,希望对您有帮助!

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

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

相关文章

MySQL-运维

1、日志 1.1 错误日志 错误日志是MysQL中最重要的日志之一&#xff0c;它记录了当mysqld启动和停止时&#xff0c;以及服务器在运行过程中发生任何严重错误时的相关信息。当数据库出现任何故障导致无法正常使用时&#xff0c;建议首先查看此日志。 该日志是默认开启的&#…

高阶C语言|结构体,枚举,联合--自定义类型的使用计算

自定义类型--结构体&#xff0c;枚举&#xff0c;联合 一、结构体1.1结构体类型的声明1.1.1结构的基础知识1.1.2结构的声明1.1.3特殊的声明 1.2结构体的引用1.3结构体变量的定义和初始化1.4结构体内存对齐1.4.1修改默认对齐数 1.5结构体传参1.6结构体实现位段&#xff08;位段的…

数据结构与算法基础-学习-26-图之MST(最小代价生成树)之Kluskal(克鲁斯卡尔)算法

最小生成树的概念、源码实现和Prim&#xff08;普利姆&#xff09;算法的概念和源码实现请参考之前的博客&#xff1a;《数据结构与算法基础-学习-25-图之MST&#xff08;最小代价生成树&#xff09;之Prim&#xff08;普利姆&#xff09;算法》 一、算法思路 Kluskal算法相较…

11.键盘事件

键盘事件 html部分 <div class"insert"><div class"key">请按下你的键盘</div> </div>css部分 * {margin: 0;padding: 0; }body {display: flex;justify-content: center;align-items: center;height: 100vh;overflow: hidden; }…

Pytorch手动实现softmax回归

参考代码&#xff1a;https://blog.csdn.net/ccyyll1/article/details/126020585 softmax回归梯度计算方式&#xff0c;特别是ij和i! j时的计算问题&#xff0c;请看如下帖子中的描述&#xff0c;这个问题是反向传播梯度计算中的一个核心问题&#xff1a;反向传播梯度计算中的…

哈工大计算机网络课程局域网详解之:MAC地址与ARP协议

哈工大计算机网络课程局域网详解之&#xff1a;MAC地址与ARP协议 文章目录 哈工大计算机网络课程局域网详解之&#xff1a;MAC地址与ARP协议MAC地址ARP&#xff1a;地址解析协议寻址&#xff1a;从一个LAN路由至另一个LAN MAC地址 在介绍MAC地址前&#xff0c;首先回顾一下之前…

SAP ABAP 实现数据库表行项目和程序加解锁功能

1.SAP ABAP 实现数据库表行项目加解锁功能 实现效果&#xff1a; 当一个数据库表以某字段为关键字段的数据被锁定时&#xff0c;同一时间其他程序无法修改改表内被锁定的数据&#xff0c;除非被解锁或退出程序。 1.事务代码&#xff1a;SE11 创建锁对象。PS&#xff1a;命名…

【计组】不同进制数之间的相互转换

前言 1、推荐在线进制转换器&#xff1a;&#xff08;都还不错&#xff09; 在线进制转换 | 进制转换器 — 在线工具 (sojson.com) 在线进制转换器 | 菜鸟工具 (runoob.com) 在线进制转换 - 码工具 (matools.com) 2、进位计数法 &#xff08;1&#xff09;二进制&#xf…

JavaScript字符串和模板字面量

● 上节课我们说明&#xff0c;号可以当作字符串连接符号使用&#xff0c;例如 const firstName "Sun"; const job "技术分享博主"; const birthYear 1991; const year 2023;const sun "我叫" firstName ",是一个" (year - bi…

线性结构:队列

文章目录 队列定义队列应用热土豆问题打印任务 队列定义 队尾进&#xff0c;队头出 队列是一种有次序的数据集合&#xff0c;其特征是新数据项的添加总发生在一端(通常称为“尾rear”端&#xff09;而现存数据项的移除总发生在另一端&#xff08;通常称为“首front”端&#x…

刷题记录-2最短路径

考点&#xff1a; 图论-最短路-Dijkstra 解题&#xff1a; c #include <iostream> #include <vector> #include <queue> using namespace std; const long long inf 0x3f3f3f3f3f3f3f3fLL; const int num 3e52; struct edge {int from,to;long long w;e…

算法竞赛入门【码蹄集新手村600题】(MT1001-1020)

算法竞赛入门【码蹄集新手村600题】(MT1001-1020&#xff09; 目录MT1001 程序设计入门MT1002 输入和输出整型数据MT1003 整数运算MT1004 求余MT1005 输入和输出实型数据MT1006 实型数运算MT1007 平均分MT1008 圆球等的相关运算MT1009 公式计算MT1010 输入和输出字符型数据MT10…

【Visual Studio】Qt 在其他 cpp 文件中调用操作 ui 界面控件

知识不是单独的&#xff0c;一定是成体系的。更多我的个人总结和相关经验可查阅这个专栏&#xff1a;Visual Studio。 还整了一个如何相互之间调用函数的文章&#xff0c;感兴趣可以看&#xff1a;【Visual Studio】Qt 在其他 cpp 文件中调用主工程下文件中的函数。 文章目录 …

第四章:包围体

第四章&#xff1a;包围体 引言-包围体&#xff08;1&#xff09;包围体测试和几何体测试&#xff08;2&#xff09;包围体测试的代价和作用&#xff08;3&#xff09;相交测试的优化&#xff08;4&#xff09;包围体相关章节和主旨 一、BV 期望特征1.1 有效的包围体1.2 包围体…

docker 网络配置详解

目录 1、docker网络模式 2、容器和容器之间是如何互通 3、容器之间互通 --link 3、自定义网络 4、不通网段的容器进行网络互通 1、docker网络模式 docker 网络模式采用的是桥接模式&#xff0c;当我们创建了一个容器后docker网络就会帮我们创建一个虚拟网卡&#xff0c;这…

Electron 学习_在进程之间通信

1.问题&#xff1a;Electron的主进程和渲染进程有着清楚的分工&#xff0c;并且不可互换。从渲染进程直接访问Node.js 接口&#xff0c;亦或者 从主进程访问HTML文档对象模型(DOM)都是不可能的 2.解决方法&#xff1a;使用进程间通信 (IPC) 可以使用 Electron 的ipcMain 模块和…

Redisson限流器RRateLimiter使用及源码分析

一、使用 使用很简单、如下 // 1、 声明一个限流器 RRateLimiter rateLimiter redissonClient.getRateLimiter(key);// 2、 设置速率&#xff0c;5秒中产生3个令牌 rateLimiter.trySetRate(RateType.OVERALL, 3, 5, RateIntervalUnit.SECONDS);// 3、试图获取一个令牌&#…

TCP首部格式【TCP原理(笔记五)】

文章目录 TCP首部格式源端口号&#xff08;Source Port&#xff09;目标端口号&#xff08;Destination Port&#xff09;序列号&#xff08;Sequence Number&#xff09;确认应答号&#xff08;Acknowledgement Number&#xff09;数据偏移&#xff08;Data Offset&#xff09…

Oracle 普通视图 (Oracle Standard Views)

视图&#xff08;views&#xff09;是一种基于表的"逻辑抽象"对象&#xff0c;由于它是从表衍生出来的&#xff0c;因此和表有许多相同点&#xff0c;我们可以和对待表一样对其进行查询/更新操作。但视图本身并不存储数据&#xff0c;也不分配存储空间。 本文只讨论普…

Linux下搭建pyqt5开发环境—基于Pycharm

防踩坑Tips&#xff1a; 1、不能学windows那样直接用pip安装PyQt5Designer和pyqt5-tools。这两个模块最根本的是用的windows的程序&#xff0c;linux上是运行不了的&#xff0c;特别是PyQt5Designer&#xff0c;会提示安装失败。 2、推荐在python环境安装同系统版本一致的pyq…