C进阶:指针的进阶(4)

news2024/11/28 12:38:17

 回调函数

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

举例:比如B函数中有一个参数是A函数的地址,在使用B函数时调用了A函数,我们就说A函数是回调函数。

下面我们通过qsort这一重要的快速排序函数来了解一下回调函数。

​
//头文件:#include <stdlib.h>

void qsort(void* base,//指向了被排序的第一个函数
	size_t sum,//排序的元素个数,size_t指无符号整数
	size_t size,//一个元素大小,单位是字节
	//函数指针类型——这个函数指针指向一个函数,能够比较base指向的两个函数
    //这个回调函数返回小于零的数表明p1<p2
    //返回大于零的数表明p1>p2
    //返回等于零的数表明p1=p2
	int (*cmp)(const void* p1, const void* p2)
);

//void*指针是无类型指针,可以接受任意类型的地址(这也是qsort函数的一个重要的特点)
//(因为这个函数不知道你要排序什么类型的指针,所以用void*)
//1.不能进行解引用操作,2.不能直接进行指针运算

​

下面来看一下qsort函数的使用(这里使用整型和结构体排序为例)

整型类型排序

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

//注意:这个函数需要自己书写(注意升降序)
int int_cmp(const void* p1, const void* p2)
{
    //由于p1,p2都是void*类型的,不能直接进行解引用操作,
    //因此将它们转换为int*再解引用获得它们的值
//这个是升序,若想改为降序,只需要将p1,p2的位置互换即可
	return (*(int*)p1 - *(int*)p2);
}

print(int sz, int arr[])
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

void test()
{
	int arr[] = { 3,1,4,5,8,6,7,9,0,2 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(int), int_cmp);
	print(sz, arr);
}

int main()
{
	test();
	return 0;
}

结构体类型排序(按照整型成员)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

int cmp_byage(const void* p1, const void* p2)
{
	return (((struct Peo*)p1)->age - ((struct Peo*)p2) -> age);
}

test1()
{
	struct Peo p[] = {{"zhangsan",20},{"lisi",50},{"wangwu",15}};
	int sz = sizeof(p) / sizeof(p[0]);
	qsort(p, sz, sizeof(struct Peo), cmp_byage);
}

int cmp_byname(const void* p1, const void* p2)
{
	return strcmp(((struct Peo*)p1)->name, ((struct Peo*)p2)->name);
}

int main()
{
	test1();
	return 0;
}

按照整型成员升序排序结果

 结构体类型排序(按照字符串成员)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

int cmp_byname(const void* p1, const void* p2)
{
	return strcmp(((struct Peo*)p1)->name, ((struct Peo*)p2)->name);
}

void test2()
{
	struct Peo p[]= { {"zhangsan",20},{"lisi",50},{"wangwu",15} };
	int sz = sizeof(p) / sizeof(p[0]);
	qsort(p, sz, sizeof(struct Peo), cmp_byname);
}

int main()
{
	test2();
	return 0;
}

按照字符串类型成员升序排序结果

 模拟实现

为了帮助我们更好地理解qsort 函数的原理,下面我会带大家来模拟实现一下这个函数。

我们给它起名起名曰:bubble_sort( )

1.使用的这个函数的思想是冒泡排序的思想。

2.它可以适用于各种类型的排序。

要搞清这个函数的具体实现方法,我们首先要思考下面几个问题:

1.对于不同类型的数据,我们万万不能使用简单的数学比较符号来比较。

解决:我们可以将2个元素的比较方法,以函数参数的形式传递,不同类型传不同比较方法。

2.不同数据类型,交换略有差异。

解决:在bubble_sort()函数内部嵌套使用一个swap函数,将比较的两个元素传递过去,由于不知道元素的类型,所以我们以char*的形式传递(因为走过char*的步长最小,我们可以根据元素大小size一个一个字节进行交换)。

下面我们来看一下具体代码(以整型数组的升序排序为例)

#include <stdio.h>
#include <string.h>

//非字符串类型的比较方法
int int_cmp(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}

//字符串类型的比较方法
//int char_cmp(const void* p1, const void* p2)
//{
//	return strcmp(根据两个字符串相关类型传入有关参数);
//}

//交换函数swap的实现
void swap(char* buf1, char* buf2, int size)
{
	int i = 0;
	char tmp = 0;
	for (i = 0; i < size; i++)
	{
		//由于不知道交换数据的具体单位,所以我么逐字节进行数据交换
		tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bubble_sort(void* base, int count, int size, int(*cmp)(void*, void*))
{
	//确定交换的趟数
	int i = 0;
	for (i = 0; i < count - 1; i++)
	{
		//确定一趟交换类型的次数
		int j = 0;
		for (j = 0; j < count - 1 - i; j++)
		{
			//这里假设是升序交换(即>0)
			//跟据交换参数的类型不同,将位置定位到第j个和第j+1个元素的位置上
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
			}
		}
	}
}

//打印
void print(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

void test()
{
	int arr[] = { 3,1,6,4,7,8,2,5,9,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), int_cmp);
	print(arr, sz);
}

int main()
{
	test();
	return 0;
}

好了,这就是回调函数及qsort函数的所有内容,感谢各位未来的大厂员工支持!!!

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

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

相关文章

Mysql教程(二):DDL学习

Mysql教程&#xff08;二&#xff09;&#xff1a;DDL学习 DDL &#xff08;Data Definition Language &#xff09;数据定义语言&#xff0c;用来定义数据库对象&#xff08;数据库、表、字段&#xff09; 1 DDL数据库操作 查询 查询所有数据库 SHOW DATABASES;查询当前数据…

利用Python与ArcGIS工具进行蒸散发ET、植被总初级生产力GPP估算

查看原文>>>基于”Python”多技术融合在蒸散发与植被总初级生产力估算中的实践应用 熟悉蒸散发ET及其组分&#xff08;植被蒸腾Ec、土壤蒸发Es、冠层截留Ei&#xff09;、植被总初级生产力GPP的概念和碳水耦合的基本原理&#xff1b;掌握利用Python与ArcGIS工具进行课…

浅谈电能分项计量在节能降耗中的应用

摘要&#xff1a;随着电力企业改革活动的持续推进&#xff0c;要想加快改革进程、优化改革效果&#xff0c;应该提高对节能降耗问题的关注度。在应用电力计量技术的过程中巧妙地渗透节能降耗这一理念&#xff0c;以此提高技术应用率&#xff0c;充分体现技术应用价值&#xff0…

对象的方法

1.Object.assign(目标对象,源对象) 用于将所有可枚举的自身属性从一个或多个源对象复制到目标对象 目标对象——应用源属性的对象&#xff0c;修改后返回。 源对象——包含你要应用的属性的对象。 返回值&#xff1a;修改后的目标对象 const target { a: 1, b: 2 }; const …

Win10我的电脑图标怎么调出来?5招搞定!

“我的电脑图标怎么突然不见啦&#xff1f;大家有没有遇到类似情况呀&#xff1f;请大家给我出出主意&#xff01;” 有的朋友可能会遇到这样一个问题&#xff0c;就是当我们更新了系统或进行某些操作后&#xff0c;计算机中【此电脑】的图标不见了。Win10我的电脑图标怎么调出…

MySQL8.1.0版本正式发布,一起尝鲜新特性

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&#x1f61…

DT人体骨骼绑定

中心点 joint31.rotateXnurbsCircle4.rotateX*0.4; 选择控制器 控制器打组 骨骼打组 手柄也变小了 没变 打组 放组中心点 没做点约束的 正确了 IK和FK 可以移动 开启IK 关闭IK 创建控制器 在开启IK FK 旋转没反应 打组&#xff0c;设置中心点 隐藏不用的属性

Spring Boot进阶(55):SpringBoot之集成MongoDB及实战使用 | 超级详细,建议收藏

1. 前言&#x1f525; 前几期我们有介绍Mysql、Redis等数据库介绍及实战演示&#xff0c;对基本的数据存放有很好的共性&#xff0c;但是如果说遇到大面积的xml、Json、bson等格式文档数据存放&#xff0c;以上数据库并非是最优选择&#xff0c;最优选择是Mongodb数据库。 那么…

【rk3568】uboot gpio寄存器配置

假设GPIO4_C6 一、查看RM手册GPIO4C6关键字搜索 其中31-16位对应低16位是否可以写&#xff0c;&#xff08;为1就有写的权限&#xff0c;如gpio4C6 中10-8将31-16中对应位写1&#xff09; 二、查看GPIO4的基地址 三、找到GPIO4的基地址 四、GPIO4C6的地址就是sys_CRFoffset(0…

Python 线程调用

简介&#xff1a; Python 线程可以通过主线程&#xff0c;调用线程来执行其他命令&#xff0c; 为Python提供更方便的使用。 并发线程测试 命令调用方式 import threading,time 定义每个线程要运行的函数 def run(n): print(“task”,n) time.sleep(1) 生成一个线程实例ta…

【kafka调试】用命令行查看kafka是否发出了命令

server 10.10.90.210:9092 topic stream_manager_center_capture_file 摄像头id&#xff1a; 17283ed2a1ac685f9fd5ef9f0de04792 cd /usr/loca/kafka bin/kafka-console-consumer.sh --bootstrap-server 10.10.90.210:9092 --topic stream_manager_center_capture_file 然后添…

ArcGIS、ENVI、InVEST、FRAGSTATS等多技术提升数据分析能力

专题一、空间数据获取与制图 1.1 软件安装与应用讲解 1.2 空间数据介绍 1.3海量空间数据下载 1.4 ArcGIS软件快速入门 1.5 Geodatabase地理数据库 专题二、ArcGIS专题地图制作 2.1专题地图制作规范 2.2 空间数据的准备与处理 2.3 空间数据可视化&#xff1a;地图符号与…

搭建gitblit

这个就是资源搭建包不是很好找 搭建一台属于自己的Git服务器_51CTO博客_git搭建本地服务器 使用wget下载安装包 wget https://miracle-1306318206.cos.ap-shanghai.myqcloud.com/public/gitblit-1.9.1.tar.gz 如果资源找不到可以使用如下链接下载&#xff1a;链接: https:/…

企业UPS不稳定?不用怕,这个技巧简单且容易!

随着技术的不断发展&#xff0c;食品行业中的UPS监控系统变得更加智能化和便捷&#xff0c;使食品生产过程更加安全可靠。 UPS监控在食品行业中扮演着关键的角色&#xff0c;确保电力供应的稳定性对于食品生产和储存过程至关重要。 客户案例 安徽某食品制造公司是一家大型食品…

shell脚本中一个隐晦的bug

1.逻辑表达式if [ ! EXP PATTERN ]不能判断EXP结果为空的情形&#xff1b; 2.而逻辑表达式if [[ EXP ! PATTERN ]可以判断EXP结果为空的情形&#xff1b;

新版本特性抢先看 | DolphinDB V2.00.10V1.30.22 即将发布

DolphinDB V2.00.10&V1.30.22 新版本即将与大家见面&#xff01; 新版本包含了 VS Code Debug、插件在线下载、TopN 系列函数、SQL标准兼容性改进等各种更新&#xff0c;以及TSDB 引擎、流计算、多种计算函数性能优化。 在此前的新版本中&#xff0c;我们也为大家提供了异…

初探C++ C++入门

目录 经典开头 — C的历史 作用域运算符 using的用法 命名空间 - namespace 命名空间的基本使用 特殊的命名空间 - 无名命名空间 全部展开和部分展开 std — C所有的标准库都在std命名空间内 省缺值 - 默认参数 占位参数 内联函数 - inline 函数重载 函数重载的用…

基于卷积神经网络的人脸笑容识别和性别识别

文章目录 前言数据集准备数据标注数据集格式转换AI Studio平台介绍及使用数据集准备创建工程模型配置模型训练 模型转化及优化模型减支模型转化 验证测试总结分析参考文献 前言 环境&#xff1a; python 3.7.9 本次项目的内容是基于卷积神经网络的人脸笑容识别和性别识别。 笑…

音频功放芯片推荐,功放特点及选型注意事项

音频放大器是用于推动扬声器发声&#xff0c;从而重现声音的功放装置&#xff0c;凡是发声的电子产品中都用到它&#xff1b;有助于增加从输入设备馈送的音频波的幅度&#xff0c;然后传输从毫瓦到千瓦的更高幅度的音频波。 目前市面上使用的电子设备都安装了放大器系统&#…

油画|艺术|三峡,诗情画意《彩云间》

《彩云间》尺寸&#xff1a;150x130cm陈可之.2006年绘清晨&#xff0c;山顶上的云霞流向天际&#xff0c;山峦与流云&#xff0c;融为一体。霞光&#xff0c;给山峰着上了五光十色&#xff0c;迷幻的光影&#xff0c;仿佛给大自然披上了一件五彩的轻纱。时空飘渺&#xff0c;给…