【C语言】指针(5)

前言:上篇文章的末尾我们使用了转移表来解决代码冗余的问题,那我们还有没有什么办法解决代码冗余呢?有的这就是接下来要说的回调函数。
往期文章:
指针1
指针2
指针3
指针4

文章目录

  • 一,回调函数
  • 二,qsort实现快速排序
    • 1,void*指针
  • 三,qsort的模拟实现

一,回调函数

先来回顾一下上篇文章末尾的内容,写一个模拟计算的代码:

#include <stdio.h> 
int add(int a, int b) 
{ return a + b; 
}
int sub(int a, int b) 
{ return a - b; 
}
int main() 
{ int x, y; int input = 1; int ret = 0; do{ printf("*************************\n");printf("*******1:add 2:sub ******\n");printf("*******   0:exit   ******\n");printf("*************************\n");printf("请选择:");scanf("%d", &input); switch (input) {case 1: printf("输⼊操作数:"); scanf("%d %d", &x, &y); ret = add(x, y); printf("ret = %d\n", ret); break; case 2: printf("输入操作数:"); scanf("%d %d", &x, &y); ret = sub(x, y); printf("ret = %d\n", ret); break; case 0: printf("退出程序\n"); break; default: printf("选择错误\n"); break; } } while (input); return 0; 
}

我们可以看到代码是非常冗余的,想要解决冗余的问题方法一在我们上一篇文章指针4(转移表)。方法二我们使用回调函数来实现。
那什么是回调函数呢?

回调函数说的是,如果你把函数的指针(地址)作为参数传递给另外一个函数时,当这个指针被用来调用其所指向的函数时,被掉用的函数就称为回调函数。

我们给出具体的代码:

#include<stdio.h>
int add(int x, int y)
{return x + y;
}
int sub(int x, int y)
{return x - y;
}
void clac(int (*pf)(int, int))
{int x = 0, y = 0;printf("请输入两个数:");scanf("%d %d", &x, &y);int ret = (*pf)(x, y);printf("计算结果为:%d\n", ret);
}
int main()
{int x, y;int input = 1;int ret = 0;do {printf("*************************\n");printf("*******1:add 2:sub ******\n");printf("*******   0:exit   ******\n");printf("*************************\n");printf("请选择:");scanf("%d", &input);switch (input){case 1:clac(add);break;case 2:clac(sub);break;case 0:printf("退出程序\n");}} while (input);
}

通过上面的两段代码对比我们有两个改进之处:
一,将case语句中那些重复性的语句封装到了一个新建的函数中。
二,函数调用发生了变化,从原来的直接调用add函数sub函数变成了先调用clac函数再去调用add函数sub函数。这种函数称之为回调函数。

看明白这两点相信已经不难理解回调函数了,但是还有一点需要注意:回调函数不是由该函数的实现方直接调用,而是在特定的事件或条 件发生时由另外的一方调用的,用于对该事件或条件进行响应。
下面画一张图让你更好的理解:
在这里插入图片描述


二,qsort实现快速排序

在前面的文章中我们介绍了冒泡排序,这次我们来介绍一些qsort。q即quick快速的,sort是排序的意思。所以qsort就是快速排序俗称快排。
那怎么使用qsort来实现快速排序呢?首先我们先得了解一些qsort:

在这里插入图片描述
![在这里插入图片描述

void qsort( 
void *base, 
size_t num, 
size_t width, 
int (__cdecl *compare )(const void *elem1, const void *elem2 ) );

通过专业文献对qsort的描述我们知道qsort共有4个参数第一个参数

  1. void *base 是代表要传入的目标数组名即所需要排序的数组名。
  2. size_t num 是代表要传入的数组内有多少个元素。
  3. size_t width 是代表要传入的数组内元素的大小。
  4. int (*compare )(const void *elem1, const void *elem2 ) 是一个函数指针,函数指针的两个参数 类型都为void*也就是说在第四个参数中我们要传入一个函数且这个函数具有比较elem1elem2这两个元素大小的功能。

我们写一个排序整型数组的代码来给大家举例,让大家能更好的了解qsort的使用方法:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
/*
void qsort(void* base, //第一个参数为需要比较的数组的首地址size_t num,  //第二个参数为该数组的元素个数size_t width, //第三个参数为该数组每个元素的大小int(__cdecl* compare)(const void* elem1, const void* elem2));//第四个参数为函数指针,及传入一个能比较数组内部元素大小的函数的地址
*///实现比较两个函数大小的函数
int comp_int(const void* elem1, const void* elem2)
{return *(int*)elem1 - *(int*)elem2;//void*的指针不能直接使用 要强转后再解引用才能使用 //elem1>elem2  返回大于零的数//elem==elem2 返回0//elem1<elem2  返回小于零的数
}
//打印整型数组
void print(int* P_arr, int sz)//一维数组传参传过去的是数组的首地址
{int i = 0;for (i = 0;i < sz;i++){printf("%d ", *(P_arr + i));}
}
//排整型的数组
int test1()
{int arr1[10] = { 1,4,3,2,6,5,8,7,9,10 };int sz = sizeof(arr1) / sizeof(arr1[0]);qsort(arr1, sz, sizeof(arr1[0]), comp_int);//print(arr1, sz);
}
int main()
{//写一个test1来测试qsort排序整型数组test1();return 0;
}

我们来分析代码的含义:

在这里插入图片描述

1,void*指针

上面诸多地方提到了void*指针,下面我们给出解释:

在指针类型中有⼀种特殊的类型是 void* 类型的,可以理解为无具体类型的指针(或者叫泛型指 针),这种类型的指针可以用来接受任意类型地址。但是也有局限性, void*类型的指针不能直接进行指针的±整数和解引用的运算。

#include <stdio.h> 
int main() { int a = 10; int* pa = &a; char* pc = &a; return 0;
}

在上⾯的代码中,将⼀个int类型的变量的地址赋值给⼀个char*类型的指针变量。编译器会警告,是因为类型不兼容。而使用void*类型就不会有这样的问题。
我们再看看void*的指针接收别的类型的指针:

#include <stdio.h> 
int main() 
{ int a = 10; void* pa = &a; void* pc = &a; *pa = 10; *pc = 0; return 0; 
}

在这里插入图片描述

这里我们可以看到, void* 类型的指针可以接收不同类型的地址,但是无法直接进行指针运算。 那么 void* 类型的指针到底有什么用呢? ⼀般 void* 类型的指针是使⽤在函数参数的部分,用来接收不同类型数据的地址,这样的设计可以实现泛型编程的效果。

理解了void*类型,以及qsort如何使用了以后,我们就可以试着去排序一些其他类型的数据了。代码内容在下载文件处取噢。

熟练了使用qosrt来排序各种类型的数据后接下来我们就来模仿着造一个qosrt函数。

三,qsort的模拟实现

我们之前学过了冒泡排序并且知道冒泡排序有一个缺点就是只能排序固定类型的数据,而qsortt能排序任意类型的数据那我们能不能使用冒泡排序的方式来模拟实现qsort快速排序呢?答案是肯定的。
我们先写一个冒泡排序,然后再看看哪些地方需要修改:

#include<stdio.h>
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-i-1;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");
}
void test()
{int arr[10]={1,3,5,7,9,2,4,6,8,0};int sz=sizeof(arr)/sizeof(arr[0]);bubble_sort(arr,sz);print_arr(arr,sz);
}
int main()
{test();return 0;
}

那如何知道那些地方需要修改呢?通过与qsort的对比我们可以得出以下几个地方需要改造:在这里插入图片描述
那我们就先来改造参数部分:void bubble_sort(void*base,int sz,int width,int (*cmp)(const void*e1,const void*e2)) 改造之后我们发现多了两个参数,一个是 width 代表单个元素大小;一个是 int (*cmp)(const void*e1,const void*e2)
这个函数指针。

其中一个修改的地方是从原来接受int类型的数组改为了void*类型原因是方便接受任意类型的数组。

其次我们来修改比较部分,上面分析了使用一个函数指针指向一个函数然后通过函数的返回值来得到e1和e2这两个元素的大小关系,这也是为什么函数指针的返回类型是int的原因。

int comp(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}

比较函数我们依然可以这样写但是要注意一个问题,现在是我们模仿qsort的逻辑,所以在bubble_sort这个函数里边就会涉及一个传参的问题,下面我们着重来探究传参问题:在这里插入图片描述

if(arr[j]>arr[j+1])——————>if(comp((char*)base+j*width,(chr*)base+(j+1)*width)>0)
//注意base就是arr

解决了参数问题,解决了传参问题接下来就是交换数据的问题了,如果不满足我们的升序要求则需要交换那怎么交换呢?通过上面的比较得出需要交换那我们既然已经得到了要交换的两个元素的地址,就不妨再封装一个函数来专门去交换元素的内容。
在这里插入图片描述

#include<stdio.h>
#include<stdlib.h>
void swap(char* buf1, char* buf2, int width)
{int i = 0;for (i = 0;i < width;i++){char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}

解决完交换的问题我们就可以给出所有的代码啦:

int bubble_cmp(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}
void swap(char* buf1, char* buf2, int width)
{int i = 0;for (i = 0;i < width;i++){char tmp = *buf1;*buf1 = *buf2;*buf2 = tmp;buf1++;buf2++;}
}
int bubble_sort(void* base, int sz, int width, int (*cmp)(const void* e1, const void* e2))
{int i = 0;for (i = 0;i < sz-1;i++){int j = 0;for (j = 0;j < sz - 1 - i;j++){if (bubble_cmp((char*)base+j*width,(char*)base+(j+1)*width)>0){swap((char*)base + j * width, (char*)base + (j + 1) * width,width);}}}
}
void test2()
{int arr[10] = { 1,3,5,7,9,2,4,6,8,0 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(arr[0]), bubble_cmp);print(arr, sz);
}
int main()
{test2();//使用冒泡排序来模拟qsortreturn 0;
}

好了以上就是本章的全部内容啦!
感谢能够看到这里的读者,如果我的文章能够帮到你那我甚是荣幸,文章有任何问题都欢迎指出!制作不易还望给一个免费的三连,你们的支持就是我最大的动力!
在这里插入图片描述

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

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

相关文章

【python】网页批量转PDF

安装wkhtmltopdf 网站&#xff1a;wkhtmltopdf wkhtmltopdf http://www.baidu.com/ D:website1.pdf 安装pdfkit库 pip install pdfkit 批量转换代码 import os import pdfkit path_wkthmltopdf rE:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe config pdfkit.configu…

游戏引擎学习第113天

仓库:https://gitee.com/mrxiao_com/2d_game_2 黑板&#xff1a;优化的基本过程 在游戏编程中&#xff0c;优化是一个非常重要的学习内容&#xff0c;尤其是想要成为专业开发者时。优化的核心是理解代码的执行速度&#xff0c;以及如何提升其性能。在这个阶段&#xff0c;已经…

通义灵码AI程序员

通义灵码是阿里云与通义实验室联合打造的智能编码辅助工具&#xff0c;基于通义大模型技术&#xff0c;为开发者提供多种编程辅助功能。它支持多种编程语言&#xff0c;包括 Java、Python、Go、TypeScript、JavaScript、C/C、PHP、C#、Ruby 等 200 多种编码语言。 通义灵码 AI…

SeaTunnel社区「Demo方舟计划」首期活动上线—— MySQL CDC实时同步至PostgreSQL实战

引言 凌晨2点&#xff0c;某电商公司的数据工程师小李正对着屏幕抓狂——业务部门临时要求将MySQL的订单表实时同步到PostgreSQL进行分析&#xff0c;众所周知&#xff0c;在数据驱动的业务场景中&#xff0c;异构数据源同步是高频刚需。 以MySQL到PostgreSQL的CDC同步为例&a…

iNeuOS工业互联网操作系统,民爆远程运维平台案例

iNeuOS工业互联网操作系统,民爆远程运维平台案例 目 录 1. 概述... 2 2. iNeuOS在民爆生产厂区和北京运维中心配置... 3 1.1 生产厂区配置... 3 1.2 运维中心配置... 7 1. 概述 针对本项目进行初步调研,项目的总体需求为满足新建…

利用websocket检测网络连接稳定性

浏览器中打开F12&#xff0c;控制台中输入以下内容 > 回车 > 等待结果 连接关闭 表示断网 let reconnectDelay 1000; // 初始重连间隔 let pingInterval null; let socketManuallyClosed false; // 标志是否手动关闭function createWebSocket() {if (socketManuallyCl…

Unity shader glsl着色器特效之 模拟海面海浪效果

一个简单的海浪效果&#xff0c;通过波的叠加实现水面起伏的动效&#xff0c;根据波峰斜率来为浪花着色&#xff0c;再根据法线贴图和水花贴图来和调整uv的平滑移动来增强海浪移动的细节。如果需要更逼真的效果可以考虑在满足浪花触发的地方添加粒子系统 前置效果图 因为是很久…

智能经济与个人智能助理有什么发展

智能经济与个人智能助理有什么发展 技术融合创新 研究个人助理与新兴技术&#xff08;如量子计算、边缘计算&#xff09;融合&#xff0c;分析对智能经济的推动作用。探索量子计算提升数据处理速度&#xff0c;边缘计算降低延迟&#xff0c;提升个人助理性能的机制&#xff0…

spring日志

前言 入门 这些就是日志 现在开始使用一下 spring是集合了日志的 注意选这个 这样我们就创建好了一个日志对象了 我们就可以这样打印日志了 日志和普通的打印消息相比&#xff0c;区别就是多个一些时间之类的消息 从左到右分别是时间&#xff0c;级别&#xff0c;PID&#x…

整合Salesmart/WhatsApp、开源Odoo模块和Deepseek AI能力,实现针对国外客户的智能客服和个性化推荐服务

一、项目背景 本文提出了一套针对软管制造公司的智能客服与个性化推荐系统实施方案&#xff0c;旨在通过整合开源Odoo模块、Salesmart/WhatsApp以及Deepseek AI能力&#xff0c;打造一个724小时不间断服务的智能化平台&#xff0c;专注于服务国外客户。方案围绕实现不间断服务…

Java中JDK、JRE,JVM之间的关系

Java中的JDK、JRE和JVM是三个核心概念&#xff0c;其关系可概括为JDK > JRE > JVM&#xff0c;具体如下&#xff1a; 一、定义与作用 JDK&#xff08;Java Development Kit&#xff09; 定义&#xff1a;Java开发工具包&#xff0c;用于开发和编译Java程序。包含内容&…

用C++ Qt实现安卓电池充电动效 | 打造工业级电量控件

一、为什么需要自定义电池控件&#xff1f; 在工业控制、车机系统、智能硬件等领域的UI开发中&#xff0c;电池状态显示是高频出现的UI组件。通过实现一个支持颜色渐变、动态充电动画、警戒阈值提示的电池控件&#xff0c;开发者可以系统掌握以下核心能力&#xff1a; Qt绘图…

Django+Vue3全栈开发实战:从零搭建博客系统

文章目录 1. 开发环境准备2. 创建Django项目与配置3. 设计数据模型与API4. 使用DRF创建RESTful API5. 创建Vue3项目与配置6. 前端页面开发与组件设计7. 前后端交互与Axios集成8. 项目优化与调试9. 部署上线10. 总结与扩展10.1 项目总结10.1.1 技术栈回顾10.1.2 项目亮点 10.2 扩…

Django 5实用指南(五)模板系统

Django5的模板系统是其核心功能之一&#xff0c;允许开发者将动态数据嵌入到HTML模板中&#xff0c;并根据不同的业务需求渲染页面。Django模板系统基于 Django模板语言&#xff08;DTL&#xff09;&#xff0c;它提供了一些强大的功能&#xff0c;如模板标签、过滤器、条件语句…

uni-app开发app时 使用uni.chooseLocation遇到的问题

问题一&#xff1a;不显示 问题二&#xff1a;选择地址列表一直在加载中 因为 uni-app 接口文档 中已经说明&#xff0c;使用腾讯的话需要开启云服务&#xff0c;具体可看官网&#xff0c;这就是为什么使用时直接不显示的原因&#xff0c;所以我使用的高德&#xff0c;但又出现…

推荐系统-排序模型

本次学习的重点是FM系列和WideNDeep系列。其实这两个模型是存在因果关系的。从最初的LR模型开始&#xff0c;因为缺失高效的特征交互方式&#xff0c;产生了FM模型&#xff0c;即通过向量内积代替特征之间的两两交互的参数。最后DNN的引入可以建模更高阶的特征。但是DNN如何与F…

体验用ai做了个python小游戏

体验用ai做了个python小游戏 写在前面使用的工具2.增加功能1.要求增加视频作为背景。2.我让增加了一个欢迎页面。3.我发现中文显示有问题。4.我提出了背景修改意见&#xff0c;欢迎页面和结束页面背景是视频&#xff0c;游戏页面背景是静态图片。5.提出增加更多游戏元素。 总结…

c#爬取数据并解析json

安装 Newtonsoft.Json Install-Package Newtonsoft.Json代码 HttpClient client new HttpClient();// 获取网页内容HttpResponseMessage response client.GetAsync("https://opentdb.com/api.php?amount10&category18&difficultyeasy&typemultiple"…

计算机毕业设计Python农产品推荐系统 农产品爬虫 农产品可视化 农产品大数据(源码+LW文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

【分布式理论13】分布式存储:数据存储难题与解决之道

文章目录 一、数据存储面临的问题二、RAID磁盘阵列的解决方案1. RAID概述2. RAID使用的技术3. RAID的代表性等级 三、分布式存储的新思路1. 分布式存储背景与特点2. 分布式存储的组成要素 一、数据存储面临的问题 在单机系统时代&#xff0c;当数据量不断增加、硬盘空间不够时…