C语言第二十二弹---指针(六)

news2024/11/16 12:24:54

个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】

指针

1. 回调函数是什么?

2、qsort使用举例

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

2.2 使用qsort排序结构体数据

3、qsort函数的模拟实现

总结


1. 回调函数是什么?

回调函数就是⼀个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被用来调用其所指向的函数
时,被调用的函数就是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的⼀方调用的,用于对该事件或条件进行响应。
第二十一弹中我们写的计算机的实现的代码中,switch选择操作中的的一些代码是重复出现的,其中虽然执行计算的逻辑是区别的,但是输入输出操作是冗余的,有没有办法,简化⼀些呢?
因为switch选择操作中的代码,只有调用函数的逻辑是有差异的,我们可以 把调用的函数的地址以参数的形式传递过去,使用函数指针接收,函数指针指向什么函数就调用什么函数 ,这里其实使用的就是回调函数的功能。
如下:
void calc(int(*pf)(int, int))//参数为函数指针
{
 int ret = 0;
 int x, y;
 printf("输⼊操作数:");
 scanf("%d %d", &x, &y);
 ret = pf(x, y);//传入什么函数地址则调用哪个函数
 printf("ret = %d\n", ret);
}

计算数值的函数如下:

#include <stdio.h>
int add(int a, int b)
{
 return a + b;
}
int sub(int a, int b)
{
 return a - b;
}
int mul(int a, int b)
{
 return a * b;
 }
int div(int a, int b)
{
 return a / b;
}

使用回调函数改造后

void calc(int(*pf)(int, int))
{
 int ret = 0;
 int x, y;
 printf("输⼊操作数:");
 scanf("%d %d", &x, &y);
 ret = pf(x, y);
 printf("ret = %d\n", ret);
}
int main()
{
 int input = 1;
 do
 {
 printf("*************************\n");
 printf(" 1:add 2:sub \n");
 printf(" 3:mul 4:div \n");
 printf(" 0:exit \n");
 printf("*************************\n");
 printf("请选择:");
 scanf("%d", &input);
 switch (input)
 {
 case 1:
 calc(add);//在cal内部调用add(相加)函数
 break;
 case 2:
 calc(sub);//在cal内部调用sub(相减)函数
 break;
 case 3:
 calc(mul);//在cal内部调用mul(相乘)函数
 break;
 case 4:
 calc(div);//在cal内部调用div(相除)函数
 break;
 case 0:
 printf("退出程序\n");
 break;
 default:
 printf("选择错误\n");
 break;
 }
 } while (input);
 return 0;
}

使用回调函数后明显感觉代码变少了,同时也不影响可读性。

2、qsort使用举例

void qsort (void* base, //数组首元素地址
size_t num, //数组元素个数
size_t size,//数组元素类型大小,单位为字节
int (*compar)(const void*,const void*)//函数指针 如何进行比较函数
);

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

#include <stdio.h>
//qosrt函数的使⽤者得实现⼀个⽐较函数
int int_cmp(const void * p1, const void * p2)
{
 return (*( int *)p1 - *(int *) p2);//p1-p2为升序,模拟实现会讲解原因
}
int main()
{
 int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
 int i = 0;
 
 qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
 for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
 {
 printf( "%d ", arr[i]);//打印数组
 }
 printf("\n");
 return 0;
}

2.2 使用qsort排序结构体数据

struct Stu //学⽣
{
 char name[20];//名字
 int age;//年龄
 };
//假设按照年龄来⽐较
int cmp_stu_by_age(const void* e1, const void* e2)
{
 return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;//按照年龄升序
}
//strcmp - 是库函数,是专⻔⽤来⽐较两个字符串的⼤⼩的
//假设按照名字来⽐较
int cmp_stu_by_name(const void* e1, const void* e2)
{
 return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);//按照姓名升序
}
//按照年龄来排序
void test2()
{
 struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
 int sz = sizeof(s) / sizeof(s[0]);
 qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}
//按照名字来排序
void test3()
{
 struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
 int sz = sizeof(s) / sizeof(s[0]);
 qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}
int main()
{
 test2();
 test3();
 return 0;
}

3、qsort函数的模拟实现

使用回调函数,模拟实现qsort(采⽤冒泡的方式)。
注意:这里第⼀次使用  void* 的指针,讲解 void* 的作用。
根据上面qsort排序的举例,我们可知qsort函数是可以适合所有数据类型的排序函数,因此我们模拟实现qsort也需要适合所有数据类型,首先确定排序函数的参数,跟上面qsort参数相同即可,重要的是如何去实现。
 按照冒泡排序的思想,升序的条件是前面比一个比后面一个大则交换,即前面一个数-后面一个数>0,但是以前我们实现的冒泡排序只适合int类型的数据,那么如何才能让所有数据类型都适合呢?
 此处就用到了void*,void*为空指针,不能直接使用,但是可以强转为其他的任何类型
 那么此处我们应该强转成什么类型呢?直接强转成int*?很显然,如果强转为int*,那么char*,short*就不好进行转化了,因此此处转化为char*,如果要用到其他的类型,我们通过+数据类型大小就可以得到
冒泡排序模拟实现如下:
void bubble(void *base, int count , int size, int(*cmp )(void *, void *))
{
 int i = 0;
 int j = 0;
 for (i = 0; i< count - 1; i++)
 {
 for (j = 0; j<count-i-1; j++)
 {
 if (cmp ((char *) base + j*size , (char *)base + (j + 1)*size) > 0)
 {
 _swap(( char *)base + j*size, (char *)base + (j + 1)*size, size);
 //交换两个数值,按照char类型交换size次即可
 }
 }
 }
}

举例:

#include <stdio.h>
int int_cmp(const void * p1, const void * p2)
{
 return (*( int *)p1 - *(int *) p2);
//根据冒泡排序前面一个数大于后面一个数则交换,即p1>p2则交换,因此为升序
}
void _swap(void *p1, void * p2, int size)
{
 int i = 0;
 for (i = 0; i< size; i++)
 {
 char tmp = *((char *)p1 + i);
 *(( char *)p1 + i) = *((char *) p2 + i);
 *(( char *)p2 + i) = tmp;
 }
 //将每个数据按照char类型进行交换,类型大小为多少则交换多少次
}
void bubble(void *base, int count , int size, int(*cmp )(void *, void *))
{
 int i = 0;
 int j = 0;
 for (i = 0; i< count - 1; i++)
 {
 for (j = 0; j<count-i-1; j++)
 {
 //升序的条件是前面比一个比后面一个大则交换,即前面一个数-后面一个数>0
 //但是形参为void* 实参为int* 因此需要先强制转换成有意义的数据类型,但是直接强制转化为int*合适?答案是不合适,如果实参是char*,short*呢?就不能适配了,因此此处最合适的做法是强制转化成char*
 if (cmp ((char *) base + j*size , (char *)base + (j + 1)*size) > 0)
 {
 _swap(( char *)base + j*size, (char *)base + (j + 1)*size, size);
 }
 }
 }
}
int main()
{
 int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
 int i = 0;
 bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
 for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
 {
 printf( "%d ", arr[i]);
 }
 printf("\n");
 return 0;
}

总结


本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!

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

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

相关文章

【正在更新】从零开始认识语音识别:DNN-HMM混合系统语音识别(ASR)原理

摘要 | Abstract TO-BE-FILLED 1.前言 | Introduction 近期想深入了解语音识别(ASR)中隐马尔可夫模型(HMM)和深度神经网络-隐马尔可夫(DNN-HMM)混合模型&#xff0c;但是尽管网络上有许多关于DNN-HMM的介绍&#xff0c;如李宏毅教授的《深度学习人类语言处理》[1]&#xff0c;…

Minecraft的红石教程之电梯

一.前言 我记得是上初中的时候&#xff0c;就看到了这类电梯。现在我在看现在这类电梯的相关视频&#xff0c;大多是盗用创意未能领会其中的红石运作规律&#xff0c;于是我就删繁就简写了这篇。 二.步骤 1.材料 粘性活塞&#xff0c;黏液块&#xff0c;红石&#xff0c;红…

Mac电脑清空特别大型旧文件如何一键清理?

在我们的数字生活中&#xff0c;Mac电脑常常承载着大量个人资料和重要文件。但当我们决定把自己的Mac送给亲人或朋友使用时&#xff0c;面临的首要任务便是彻底且高效地清空所有个人数据&#xff0c;以保证隐私安全。传统的删除方法虽然简单&#xff0c;但往往不能彻底清除所有…

爬虫系列-web请求全过程剖析

&#x1f308;个人主页: 会编程的果子君 ​&#x1f4ab;个人格言:“成为自己未来的主人~” 上一小节我们实现了一个网页的整体抓取工作&#xff0c;那么本小节&#xff0c;给各位好好剖析一下web请求的全部过程&#xff0c;这样有助于后面我们遇到的各种各样的网站就有了入手…

H5 带网站测速引导页源码

H5 带网站测速引导页源码 源码介绍&#xff1a;一款带网站测速功能的引导页源码 下载地址&#xff1a; https://www.changyouzuhao.cn/10717.html

X图形

1.题目 这道题是蓝桥云课上面的一道题目&#xff0c;它是2022年蓝桥杯省模拟题&#xff0c;题目难度为简单。 考察的知识点为递归。 题目链接&#xff1a;X图形 2.思路 如何理解题意&#xff1f; 蓝桥杯的题目和Leetcode题目最大的不同点在于&#xff0c;蓝桥杯的题目大部…

【大厂AI课学习笔记】【1.6 人工智能基础知识】(2)机器学习

目录 必须理解的知识点&#xff1a; 举一个草莓的例子&#xff1a; 机器学习的三个类别&#xff1a; 监督学习&#xff1a; 无监督学习&#xff1a; 强化学习&#xff1a; 更多知识背景&#xff1a; 机器学习的诞生需求 监督学习的关键技术与实现步骤 无监督学习的关…

C#计算矩形面积:通过定义结构 vs 通过继承类

目录 一、涉及到的知识点 1.结构 2.结构和类的区别 3.继承 4.使用类继承提高程序的开发效率 5.属性 &#xff08;1&#xff09;属性定义 &#xff08;2&#xff09;get访问器 &#xff08;3&#xff09;set访问器 6. 属性和字段的区别 二、实例&#xff1a;通过定义…

专业138+总分400+南京航空航天大学878数电信号考研经验南航电子信息与通信,真题,大纲,参考书

经过一年的复习&#xff0c;顺利被南京航空航天大学录取&#xff0c;初试专业课878数字电路和信号与系统138&#xff0c;总分400&#xff0c;回看这一年的复习&#xff0c;从择校到考研备考经历了很多&#xff0c;也有很多想和大家分享的复习经验&#xff0c;希望对大家复习有所…

网络的基本概念和socket编程

网络的基本概念 1.协议1.1 协议的基本概念1.2 常见的协议 2.分层模型2.1网络七层OSI 7层模型&#xff1a;物数网传会表应(口诀)2.2TCP/IP模型2.3数据通信的过程2.4网络的设计模式2.5以太网帧的格式 3.SOCKET编程3.1网络字节序3.2 相关结构体和函数3.3 代码实现 1.协议 1.1 协议…

【Linux】学习-深入了解文件的读与写

深入了解语言级别(C语言)文件操作的"读"与"写" 在学习前&#xff0c;我们先要知道在Linux下的一个原则&#xff1a;一切皆是文件 如何理解呢&#xff1f;举个外设的例子&#xff0c;比如键盘和显示器&#xff0c;这两个外设也可以其实本质上也是文件&…

springboot+vue电影推荐系统 java电影院售票选座系统1r6m2

用户模块 1)注册&#xff1a;用户输入账号、密码、确认密码、昵称、手机、邮箱、简介&#xff0c;点击注册按钮&#xff0c;完成注册。 2)登录&#xff1a;用户成功输入用户账号和密码&#xff0c;点击登录按钮。 3)用户主页面&#xff1a;以用户登录成功后&#xff0c;可以查看…

基于vue+node.js的校园跳蚤市场系统多商家

校园跳蚤市场系统可以在短时间内完成大量的数据处理、帮助用户快速的查找校园跳蚤市场相关信息&#xff0c;实现的效益更加直观。校园跳蚤市场系统中采用nodejs技术和mysql数据库。主要包括管理员、发布者和用户三大部分&#xff0c;主要功能是实现对个人中心、用户管理、发布者…

机器学习复习(8)——逻辑回归

目录 逻辑函数&#xff08;Logistic Function&#xff09; 逻辑回归模型的假设函数 从逻辑回归模型转换到最大似然函数过程 最大似然函数方法 梯度下降 逻辑函数&#xff08;Logistic Function&#xff09; 首先&#xff0c;逻辑函数&#xff0c;也称为Sigmoid函数&#…

Peter算法小课堂—单调队列

祝大家新年快乐&#xff01; 今天这一次有点简单。 单调队列有两个要点&#xff0c;一个是单调&#xff0c;另一个就是我们的队列。 听到队列&#xff0c;我相信大家一定会想到它的好朋友BFS吧。但是……今天……可……没……那么……简单哦。 西佳佳偶像天团1 题目描述 …

M1 Mac使用SquareLine-Studio进行LVGL开发

背景 使用Gui-Guider开发遇到一些问题&#xff0c;比如组件不全。使用LVGL官方的设计软件开发 延续上一篇使用的基本环境。 LVGL项目 新建项目 选择Arduino的项目&#xff0c;设定好分辨率及颜色。 设计UI 导出代码 Export -> Create Template Project 导出文件如图…

1978-2023年全国国内生产总值、分产业分行业增加值相关指标数据

1978-2023年全国国内生产总值、分产业分行业增加值相关指标数据 1、时间&#xff1a;1978-2023年 2、指标&#xff1a;国内生产总值(亿元)、第一产业增加值(亿元)、第二产业增加值(亿元)、第三产业增加值(亿元)、人均国内生产总值(元)、国民总收入指数(上年100)、国内生产总值…

车载电子电器架构 —— 电子电气系统车载功能子系统

车载电子电器架构 —— 电子电气系统车载功能子系统 我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 本就是小人物&#xff0c;输了就是输了&#xff0c…

Linux 从日志中抽取信息,批量生成SQL语句并执行

这里写目录标题 一. 需求分析二. 从日志中抽取出指定字段&#xff0c;并切分为若干个子文件三. 生成查询执行计划四. 生成查询的SQL语句五. 检查并执行 一. 需求分析 有如下日志文件&#xff0c;假设日志文件中有10000条数据&#xff0c;要求将全部的TRANSACTIONID抽取出来&am…

在VSCode中创建Java项目

在VSCode中创建Java项目 首先&#xff0c;保证安装了Java的JDK. WinR -> 输入cmd -> 输入 java -version -> 然后可以看到安装的JDK版本&#xff0c;如果没安装可以去找教程。 JDK安装参考教程 打开VSCode&#xff0c;打开扩展&#xff08;Ctrl Shift S&#xff…