【C语言系列】深入理解指针(4)

深入理解指针(4)

  • 一、回调函数是什么?
  • 二、qsort使用举例
    • 2.1使用qsort函数排序整型数据
    • 2.2使用qsort排序结构数据
  • 三、qsort函数的模拟实现
  • 四、总结

一、回调函数是什么?

回调函数就是一个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数
时,被调用的函数就是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条
件发生时由另外的一方调用的,用于对该事件或条件进行响应。
上一篇文章我们实现了一个能够加减乘除和正常退出的计算器,阅读上篇文章:https://blog.csdn.net/2301_80179750/article/details/145286102?fromshare=blogdetail&sharetype=blogdetail&sharerId=145286102&sharerefer=PC&sharesource=2301_80179750&sharefrom=from_link
使用回调函数改造之前的代码:

#include <stdio.h>
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;
}
nemu()
{printf("*************************\n");printf("  1:add           2:sub  \n");printf("  3:mul           4:div  \n");printf("  0:exit                 \n");printf("*************************\n");
}
int main()
{
int input = 0;
int x = 0;
int y = 0;
int ret = 0;
//创建一个函数指针的数组
int(*pfArr[5])(int,int) = {NULL,Add,Sub,Mul,Div};
do 
{
nemu();
printf("请选择:");
scanf("%d",&input);//2
if(input >= 1 && input <= 4)
{
printf("请输入2个操作数:");
scanf("%d %d",&x,&y);
ret = pfArr[input](x,y);
printf("%d\n",ret);
}
else if(input == 0)
{
printf("退出计算器\n");
break;
}
else
{
printf("选择错误,重新选择\n");
}
}while(input);
return 0;
}

使用回调函数改造之后的代码:

#include <stdio.h>
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 nemu()
{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个操作数:");
scanf("%d %d",&x,&y);
ret = pf(x,y);
printf("%d\n",ret);
}
int main()
{
int input = 0;
do
{
nemu();
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;
}

通过观察上述代码我们可以得出结论:我们可以把调用的函数的地址以参数的形式传递过去,使用函数指针接收,函数指针指向什么函数就调用什么函数,这里其实使用的就是回调函数的功能。

二、qsort使用举例

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

qsort —— 用来排序的,库函数,直接可以用来排序数据,底层使用的是快速排序的方式。
注:qsort是库函数需要包含头文件<stdlib.h>
函数形式如下:

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

官网如图:
在这里插入图片描述
在这里插入图片描述
官网链接:https://legacy.cplusplus.com/reference/cstdlib/qsort/?kw=qsort
qsort函数有实现者;qsort函数的使用者 —— 明确的知道要排序的是什么数据,这些数据应该如何比较,所以提供两个元素的比较函数。
使用qsort函数排序整型数据,代码如下:

#include <stdio.h>
#include <stdlib.h>
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;//可以调节降序
}// > ——> >0	//< ——> <0	//== ——> ==0
void test1()
{
int arr[] = {3,1,7,8,5,2,4,9,0,6};
int sz = sizeof(arr)/sizeof(arr[0]);
qsort(arr,sz,sizeof(arr[0]),cmp_int);
print_arr(arr,sz);
}
int main()
{
//写一段代码使qsort排序整型数据
test1();
return 0;
}

运行结果如下图:
在这里插入图片描述

2.2使用qsort排序结构数据

strcmp是按照对应着字符串中的字符的ASCII码值比值(是专门用来比较两个字符串的大小的),是库函数。
注:必须包含头文件<string.h>。
在这里插入图片描述
结构体访问成员操作符:

. ->

结构体变量.成员名
结构体指针->成员名
结构体访问操作符如何使用,代码如下:

#include <stdio.h>
struct Stu
{
char name[20];
int age;
};
void print(struct Stu*ps)
{
//printf("%s %d\n",(*ps).name,(*ps).age);
printf("%s %d\n",ps -> name,ps -> age);
}
//->结构体成员的间接访问操作
//结构体指针->成员名
int main()
{
struct Stu s = {"zhangsan",18};
print(&s);
return 0;
}

使用qsort排序结构数据,代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//struct Stu
//{
//	char name[20];
//	int age;
//}s, s1, s2;//s1,s2,s是结构体变量
//typedef struct Stu
//{
//	char name[20];
//	int age;
//}stu;//stu是类型名
struct Stu
{char name[20];int age;
};
//这里的两个结构体元素怎么比较大小?
//1.按照名字比较 —— 字符串比较 —— strcmp
//2.按照年龄比较 —— 整型比较
int cmp_stu_by_name(const void* p1, const void* p2)
{return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}
int cmp_stu_by_age(const void* p1, const void* p2)
{return((struct Stu*)p1)->age - ((struct Stu*)p2)->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_name);qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}
int main()
{test2();return 0;
}

调试如下图:
在这里插入图片描述
通过调试,我们可以看出来qsort对结构体进行排序了。

三、qsort函数的模拟实现

使用回调函数,模拟实现qsort(采用冒泡的方式)。
qsort函数的模拟实现,代码如下:

#include <stdio.h>
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;
}
void Swap(char*buf1,char*buf2,size_t 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++)
{
//比较
if(cmp((char*)base + j * width,(char*)base + (j + 1)*width) > 0)//改变
{
//交换两个元素
Swap((char*)base + j * width,(char*)base + (j + 1)*width,width);
}
}
}
}
//测试的是bubble_sort排序整型数据
void test1()
{
int arr[] = {3,1,7,8,5,2,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()
{
test1();
return 0;
}

运行结果如下图:
在这里插入图片描述

四、总结

这篇文章详细介绍了C语言中的回调函数和qsort函数的使用,通过具体的代码示例展示了如何利用这些技术实现灵活的函数调用和数据排序。
回调函数
回调函数是一种通过函数指针调用的函数。在C语言中,函数指针允许我们将函数的地址作为参数传递给另一个函数,从而在特定的事件或条件下调用这些函数。文章通过一个简单的计算器程序展示了回调函数的使用。原始代码中,通过一个函数指针数组来选择不同的运算函数。改造后的代码中,使用了回调函数,将运算函数的地址作为参数传递给Calc函数,从而实现了更灵活的函数调用。这种改造不仅提高了代码的可读性,还增强了程序的灵活性和可扩展性。
qsort函数
qsort是C标准库中的一个通用排序函数,可以对任意类型的数据进行排序。文章首先介绍了qsort函数的基本用法,包括其参数的含义和如何定义比较函数。通过一个整型数组的排序示例,展示了如何使用qsort对整型数据进行排序。接着,文章进一步介绍了如何使用qsort对结构体数组进行排序。通过定义不同的比较函数,可以按照结构体中的不同成员进行排序,如按名字或年龄排序。这些示例展示了qsort函数的强大功能和灵活性。
qsort函数的模拟实现
文章最后通过一个冒泡排序的实现,模拟了qsort函数的行为。这个模拟实现使用了回调函数来比较元素,从而实现了对不同类型数据的排序。通过这个模拟实现,读者可以更好地理解qsort函数的内部工作机制,以及如何通过回调函数实现灵活的数据比较。
这篇文章通过具体的代码示例,详细介绍了C语言中的回调函数和qsort函数的使用。通过回调函数,可以实现更灵活的函数调用,提高代码的可读性和可扩展性。qsort函数则提供了一种通用的排序方法,可以对任意类型的数据进行排序。通过这些技术,读者可以更好地理解和应用C语言中的函数指针和排序算法。

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

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

相关文章

vim的多文件操作

[rootxxx ~]# vim aa.txt bb.txt cc.txt #多文件操作 next #下一个文件 prev #上一个文件 first #第一个文件 last #最后一个文件 快捷键: ctrlshift^ #当前和上个之间切换 说明&#xff1a;快捷键ctrlshift^&#xff0c…

解决CentOS9系统下Zabbix 7.2图形中文字符乱码问题

操作系统&#xff1a;CentOS 9 Zabbix版本&#xff1a;Zabbix7.2 问题描述&#xff1a;主机图形中文字符乱码 解决方案&#xff1a; # 安装字体配置和中文语言包 sudo yum install -y fontconfig langpacks-zh_CN.noarch # 检查是否已有中文字体&#xff1a; fc-list :lan…

[SUCTF 2018]MultiSQL1

进去题目页面如下 发现可能注入点只有登录和注册,那么我们先注册一个用户&#xff0c;发现跳转到了/user/user.php&#xff0c; 查看用户信息,发现有传参/user/user.php?id1 用?id1 and 11,和?id1 and 12,判断为数字型注入 原本以为是简单的数字型注入&#xff0c;看到大…

计算机视觉-卷积

卷积-图像去噪 一、图像 二进制 灰度 彩色 1.1二进制图像 0 1 一个点可以用一个bit&#xff08;0/1&#xff09;来表示 1.2灰度图像 0-255 一个点可以用一个byte来表示 1.3彩色图像 RGB 表达一个彩色图像先说它的分辨率p/w&#xff08;宽&#xff09;和q/h&#xff08;高…

mybatis(78/134)

前天学了很多&#xff0c;关于java的反射机制&#xff0c;其实跳过了new对象&#xff0c;然后底层生成了字节码&#xff0c;创建了对应的编码。手搓了一遍源码&#xff0c;还是比较复杂的。 <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE …

1.23 补题 寒假训练营

E 一起走很长的路&#xff01; 输入描述 第一行输入两个整数 n,q&#xff08;1≤n,q≤210^5&#xff09;&#xff0c;代表多米诺骨牌的个数和询问次数。 第二行输入 n 个整数 a1,a2,…,an​&#xff08;1≤ai≤10^9&#xff09;&#xff0c;表示多米诺骨牌的重量。 此后输入…

【中间件快速入门】什么是Redis

现在后端开发会用到各种中间件&#xff0c;一不留神项目可能在哪天就要用到一个我们之前可能听过但是从来没接触过的中间件&#xff0c;这个时候对于开发人员来说&#xff0c;如果你不知道这个中间件的设计逻辑和使用方法&#xff0c;那在后面的开发和维护工作中可能就会比较吃…

金晟新能源由盈转亏:毛利率下滑产能利用率不佳,关联交易持续增加

《港湾商业观察》黄懿 近期&#xff0c;广东金晟新能源股份有限公司&#xff08;下称“金晟新能源”&#xff09;递交了招股书&#xff0c;拟冲刺港交所IPO&#xff0c;中金公司、招银国际为联席保荐人。 金晟新能源处于电池回收的新兴大势行业&#xff0c;但是&#xff0c;受…

RTMP|RTSP播放器只解码视频关键帧功能探讨

技术背景 我们在做RTMP|RTSP直播播放器的时候&#xff0c;遇到过这样的技术诉求&#xff0c;在一些特定的应用场景中&#xff0c;可能只需要关键帧的信息&#xff0c;例如视频内容分析系统&#xff0c;可能只对关键帧进行分析&#xff0c;以提取特征、检测对象或场景变化。鉴于…

2K高刷电竞显示器怎么选?

2K高刷电竞显示器怎么选&#xff1f;哪个价格适合你&#xff1f;哪个配置适合你呢&#xff1f; 1.HKC G27H2Pro - 2K高刷电竞显示器怎么选 外观设计 - HKC G27H2Pro 2K高刷电竞显示器 电竞风拉满&#xff1a;作为猎鹰系列的一员&#xff0c;背部 “鹰翼图腾” 切割线搭配炎红…

STM32-时钟树

STM32-时钟树 时钟 时钟

基于SpringBoot+WebSocket的前后端连接,并接入文心一言大模型API

前言&#xff1a; 本片博客只讲述了操作的大致流程&#xff0c;具体实现步骤并不标准&#xff0c;请以参考为准。 本文前提&#xff1a;熟悉使用webSocket 如果大家还不了解什么是WebSocket&#xff0c;可以参考我的这篇博客&#xff1a; rWebSocket 详解&#xff1a;全双工…

StarRocks BE源码编译、CLion高亮跳转方法

阅读SR BE源码时&#xff0c;很多类的引用位置爆红找不到&#xff0c;或无法跳转过去&#xff0c;而自己的Linux机器往往缺乏各种C依赖库&#xff0c;配置安装比较麻烦&#xff0c;因此总体的思路是通过CLion远程连接SR社区已经安装完各种依赖库的Docker容器&#xff0c;进行编…

STM32 按键密码系统的实现

本次基于STM32F407开发板&#xff0c;来实现密码系统&#xff0c;输入四位密码&#xff0c;密码正确时LED1亮&#xff0c;密码错误时四个LED灯双闪。 LED双闪代码 简单的逻辑&#xff0c;让四个LED灯先亮然后再延时一会LED灯灭&#xff0c;循环4此实现双闪的效果。 按键密码的…

linux常用加固方式

目录 一.系统加固 二.ssh加固 三.换个隐蔽的端口 四.防火墙配置 五.用户权限管理 六.暴力破解防护 七.病毒防护 八.磁盘加密 九.双因素认证2FA 十.日志监控 十一.精简服务 一.系统加固 第一步&#xff1a;打好系统补丁 sudo apt update && sudo apt upgra…

hadoop==docker desktop搭建hadoop

hdfs map readuce yarn https://medium.com/guillermovc/setting-up-hadoop-with-docker-and-using-mapreduce-framework-c1cd125d4f7b 清理资源 docker-compose down docker system prune -f

【Elasticsearch 基础入门】Centos7下Elasticsearch 7.x安装与配置(单机)

Elasticsearch系列文章目录 【Elasticsearch 基础入门】一文带你了解Elasticsearch&#xff01;&#xff01;&#xff01;【Elasticsearch 基础入门】Centos7下Elasticsearch 7.x安装与配置&#xff08;单机&#xff09; 目录 Elasticsearch系列文章目录前言单机模式1. 安装 J…

Fullcalendar @fullcalendar/react 样式错乱丢失问题和导致页面卡顿崩溃问题

问题描述&#xff1a; 我使用 fullcalendar的react版本时&#xff0c;出现了一个诡异的问题&#xff0c;当我切换到 一个iframe页面时&#xff08;整个页面是一个iframe嵌入的&#xff09;&#xff0c;再切换回来日历的样式丢失了&#xff01;不仅丢失了样式还导致页面崩溃了&…

算法12(力扣739)-每日温度

1、问题 给定一个整数数组 temperatures &#xff0c;表示每天的温度&#xff0c;返回一个数组 answer &#xff0c;其中 answer[i] 是指对于第 i 天&#xff0c;下一个更高温度出现在几天后。如果气温在这之后都不会升高&#xff0c;请在该位置用 0 来代替。 2、示例 &#…