【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.rhkb.cn/news/21587.html

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

相关文章

SpringBoot:SSL证书部署+SpringBoot实现HTTPS安全访问

一、前言 SSL协议介于TCP/IP协议栈的第四层&#xff08;传输层&#xff09;和第七层&#xff08;应用层&#xff09;之间&#xff0c;为基于TCP的应用层协议&#xff08;如HTTP&#xff09;提供安全连接。它通过在客户端和服务器之间建立一个加密的通道&#xff0c;确保数据在传…

嵌入式 lwip http server makefsdata

背景&#xff1a; 基于君正X2000 MCU Freertoslwip架构 实现HTTP server服务&#xff0c;MCU作为HTTP服务器通过网口进行数据包的传输&#xff0c;提供网页服务。其中设计到LWIP提供的工具makefsdata&#xff0c;常用于将文件或目录结构转换为适合嵌入到固件中的二进制格式。 …

论文笔记-WSDM2025-ColdLLM

论文笔记-WSDM2025-Large Language Model Simulator for Cold-Start Recommendation ColdLLM&#xff1a;用于冷启动推荐的大语言模型模拟器摘要1.引言2.前言3.方法3.1整体框架3.1.1行为模拟3.1.2嵌入优化 3.2耦合漏斗ColdLLM3.2.1过滤模拟3.2.2精炼模拟 3.3模拟器训练3.3.1LLM…

《DeepSeek-V3:人工智能大语言模型》

《DeepSeek-V3:人工智能大语言模型》 1. 引言 我们介绍了 DeepSeek-V3,这是一个强大的专家混合 (MoE) 语言模型,总共有 671B 个参数,每个令牌激活了 37B。 为了实现高效的推理和具有成本效益的训练,DeepSeek-V3 采用了多头潜在注意力 (MLA) 和 DeepSeekMoE 架构,这些…

手机控制电脑远程关机

远程看看软件兼容iOS和Android设备&#xff0c;该软件除了能通过电脑远程关闭另一台电脑外&#xff0c;您还可以通过它在手机上远程关闭公司的电脑。您可以按照以下步骤进行操作以实现电脑远程关机&#xff1a; 步骤1.在手机应用商店搜索“远程看看”进行软件安装&#xff0c;…

Aseprite绘画流程案例(1)——画相机图标

原图&#xff1a; 步骤一&#xff1a;打开需要参照的图标 步骤二&#xff1a;将参照的图片拖放到右边&#xff0c;作为参考 步骤三&#xff1a;新建24x24的画布&#xff0c;背景为白色的画布 步骤四&#xff1a;点击菜单栏——视图——显示——像素网格&#xff08;如果画布已经…

The Heliosphere 日球层

转自 The Heliosphere - NASA This is an artists concept of our Heliosphere as it travels through our galaxy with the major features labeled. Termination Shock: Blowing outward billions of kilometers from the Sun is the solar wind, a thin stream of electrica…

使用API有效率地管理Dynadot域名,为域名部署DNS安全拓展(DNSSEC)

关于Dynadot Dynadot是通过ICANN认证的域名注册商&#xff0c;自2002年成立以来&#xff0c;服务于全球108个国家和地区的客户&#xff0c;为数以万计的客户提供简洁&#xff0c;优惠&#xff0c;安全的域名注册以及管理服务。 Dynadot平台操作教程索引&#xff08;包括域名邮…

vsan数据恢复—vsan缓存盘故障导致虚拟磁盘文件丢失的数据恢复案例

vsan数据恢复环境&故障&#xff1a; VMware vsan架构采用21模式。每台设备只有一个磁盘组&#xff08;71&#xff09;&#xff0c;缓存盘的大小为240GB&#xff0c;容量盘的大小为1.2TB。 由于其中一台主机&#xff08;0号组设备&#xff09;的缓存盘出现故障&#xff0c;导…

匹配算法:向下就近原则,向下没有就向上

匹配算法&#xff1a;向下就近原则&#xff0c;向下没有就向上 实现方式一实现方式二总结 实现方式一 private static List<Integer> findMatches(List<Integer> sourceList, List<Integer> searchValues) {List<Integer> sortedList sourceList.stre…

AI客服-接入deepseek大模型到微信(本地部署deepseek集成微信自动收发消息)

1.本地部署 1.1 ollama Ollama软件通过其高度优化的推理引擎和先进的内存管理机制&#xff0c;显著提升了大型语言模型在本地设备上的运行效率。其核心采用了量化技术&#xff08;Quantization&#xff09;以降低模型的计算复杂度和存储需求&#xff0c;同时结合张量并行计算&…

Python VsCode DeepSeek接入

Python VsCode DeepSeek接入 创建API key 首先进入DeepSeek官网&#xff0c;https://www.deepseek.com/ 点击左侧“API Keys”&#xff0c;创建API key&#xff0c;输出名称为“AI” 点击“创建"&#xff0c;将API key保存&#xff0c;复制在其它地方。 在VsCode中下载…

【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…

架构师面试(三):订阅模型

问题 对【注册中心】【配置中心】【消息队列】和【IM】进行分析和抽象&#xff0c;可归纳出一个完整的业务模型单元&#xff0c;即【订阅系统】&#xff0c;下面关于实现订阅系统的几种模型的相关描述中&#xff0c;说法正确的有哪几项&#xff1f; A. 信箱模型&#xff0c;即…

数据结构:算法的时间复杂度和空间复杂度

1.算法效率 1.1 如何衡量一个算法的好坏 如何衡量一个算法的好坏呢&#xff1f; 比如对于以下斐波那契数列&#xff1a; long long Fib(int N) {if(N < 3)return 1;return Fib(N-1) Fib(N-2); }斐波那契数列的递归实现方式非常简洁&#xff0c;但简洁一定好吗&#xff…

linux下pip下载项目失败

想下载CLIP的项目复现代码的时候&#xff0c;出现问题如下&#xff1a; 于是手动使用 Git 克隆仓库&#xff0c; git clone https://github.com/openai/CLIP.git cd CLIP pip install .ls查看文件如下&#xff1a;(手动克隆git项目成功)

Redis文档总结

文档&#xff1a;https://redis.com.cn/topics/why-use-redis.html 1.我们为什么一定要用 Redis 呢&#xff1f; 速度快&#xff0c;完全基于内存&#xff0c;使用 C 语言实现&#xff0c;网络层使用 epoll 解决高并发问题&#xff0c;单线程模型避免了不必要的上下文切换及竞争…

【前端框架】Vue3 面试题深度解析

本文详细讲解了VUE3相关的面试题&#xff0c;从基础到进阶到高级&#xff0c;分别都有涉及&#xff0c;希望对你有所帮助&#xff01; 基础题目 1. 简述 Vue3 与 Vue2 相比有哪些主要变化&#xff1f; 答案&#xff1a; 响应式系统&#xff1a;Vue2 使用 Object.definePrope…

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 扩…

【论文笔记】MambaGlue: Fast and Robust Local Feature Matching With Mamba

【引用格式】&#xff1a;Ryoo K, Lim H, Myung H. MambaGlue: Fast and Robust Local Feature Matching With Mamba[J]. arXiv preprint arXiv:2502.00462, 2025. 【网址】&#xff1a;https://arxiv.org/pdf/2502.00462 【开源代码】&#xff1a;https://github.com/uri-Ka…