news 2026/5/16 19:07:33

吃透指针通用用法:回调函数与 qsort 的使用和模拟

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
吃透指针通用用法:回调函数与 qsort 的使用和模拟

🏠个人主页:黎雁
🎬作者简介:C/C++/JAVA后端开发学习者
❄️个人专栏:C语言、数据结构(C语言)、EasyX、游戏、规划
✨ 从来绝巘须孤往,万里同尘即玉京

文章目录

    • 前景回顾:前四篇指针核心速记 📝
    • 一、回调函数:通过函数指针调用的函数 📞
      • 1. 回调函数的定义与理解
      • 2. 代码示例:计算器中的回调函数
      • 3. 生活类比理解回调函数
    • 二、qsort函数:通用排序的利器 ⚡
      • 1. qsort函数的原型
      • 2. 比较函数的规则
      • 3. qsort的使用案例
        • 案例1:排序整型数组
        • 案例2:排序结构体数组
    • 三、模拟实现qsort:基于冒泡排序的通用改造 🔧
      • 1. 核心改造思路
      • 2. 完整模拟实现代码
    • 写在最后 📝

指针系列的倒数第二篇来啦!这一篇我们将聚焦指针的终极实战用法——回调函数,同时深度解析库函数qsort的使用方法和模拟实现,帮你彻底掌握指针在通用算法中的灵活应用,为下一篇的笔试面试题精讲做好充分准备!

前景回顾:前四篇指针核心速记 📝

指针第一讲:从内存到运算,吃透指针核心逻辑
指针第二讲:const 修饰、野指针规避与传址调用
指针第三讲:数组与指针深度绑定 + 二级指针 + 指针数组全解析
指针第四讲:字符指针、数组指针、函数指针及转移表应用

想要吃透本篇的实战内容,先回顾前四篇的关键知识点:

  1. 指针本质是地址,不同类型的指针指向不同的目标对象,包括变量、数组、函数。
  2. 数组与指针深度绑定,数组传参本质传递首元素地址;函数指针可存储函数地址,实现对函数的间接调用。
  3. 函数指针数组可以构建转移表,简化多分支逻辑;typedef可重命名复杂的指针类型,提升代码可读性。

一、回调函数:通过函数指针调用的函数 📞

回调函数是C语言中一种重要的编程思想,它的核心是把函数的地址作为参数传递给另一个函数,在合适的时机通过函数指针调用这个函数。这一知识点也是笔面试中的高频考点。

1. 回调函数的定义与理解

  • 回调函数不是由函数的实现者直接调用,而是由其他函数通过函数指针间接调用。
  • 回调函数的出现,让程序的逻辑分层更清晰,也让功能的拓展更灵活。

2. 代码示例:计算器中的回调函数

以简易计算器为例,AddSub等功能函数就是回调函数,它们的地址被传递给calc函数,在calc函数中被调用。

#include<stdio.h>// 功能函数——回调函数intAdd(intx,inty){returnx+y;}intSub(intx,inty){returnx-y;}// 中间层函数:接收函数指针,调用回调函数voidcalc(int(*p)(int,int)){intx=0,y=0,r=0;printf("请输入两个操作数:");scanf("%d %d",&x,&y);r=p(x,y);// 通过函数指针调用回调函数printf("计算结果:%d\n",r);}// 菜单函数voidmenu(){printf("*************************\n");printf("*** 1.add 2.sub ***\n");printf("*** 0.exit ***\n");printf("*************************\n");}intmain(){intinput=0;do{menu();printf("请选择:");scanf("%d",&input);switch(input){case1:calc(Add);// 传递Add函数地址break;case2:calc(Sub);// 传递Sub函数地址break;case0:printf("退出程序\n");break;default:printf("选择错误\n");break;}}while(input);return0;}

3. 生活类比理解回调函数

回调函数的逻辑就像酒店的叫醒服务

  1. 用户(主函数)告诉前台(中间层函数)叫醒的时间和方式(传递回调函数地址)。
  2. 到了指定时间,前台(中间层函数)按照用户要求的方式(调用回调函数)叫醒用户。
  3. 用户不需要自己定闹钟,只需要提供“叫醒方式”,前台负责执行。

二、qsort函数:通用排序的利器 ⚡

qsort是C语言标准库中的排序函数,基于快速排序算法实现,可以排序任意类型的数组,包括整型、字符型、结构体等,其核心就是借助回调函数实现通用比较逻辑。qsort的使用与模拟实现是笔面试的重点考察内容。

1. qsort函数的原型

使用qsort需要包含头文件<stdlib.h>,函数原型如下:

voidqsort(void*base,// 待排序数组的首元素地址size_tnum,// 待排序数组的元素个数size_tsize,// 数组中每个元素的大小(单位:字节)int(*compar)(constvoid*,constvoid*)// 比较两个元素的回调函数);

2. 比较函数的规则

qsort的第四个参数是一个函数指针,指向的比较函数需要遵循固定规则:

  • p1指向的元素 >p2指向的元素,返回大于0的数。
  • p1指向的元素 ==p2指向的元素,返回0
  • p1指向的元素 <p2指向的元素,返回小于0的数。
  • 函数参数是const void*类型,可接收任意类型的地址,使用时需强制类型转换。

3. qsort的使用案例

案例1:排序整型数组
#include<stdio.h>#include<stdlib.h>// 整型比较函数(升序)intcmp_int(constvoid*p1,constvoid*p2){// void* 不能直接解引用,需强制转换为int*return*(int*)p1-*(int*)p2;}// 整型比较函数(降序)// int cmp_int(const void* p1, const void* p2)// {// return *(int*)p2 - *(int*)p1;// }// 打印数组voidprint_arr(intarr[],intsz){inti=0;for(i=0;i<sz;i++){printf("%d ",arr[i]);}printf("\n");}voidtest1(){intarr[]={9,7,5,3,1,2,4,6,8,0};intsz=sizeof(arr)/sizeof(arr[0]);qsort(arr,sz,sizeof(arr[0]),cmp_int);printf("排序后:");print_arr(arr,sz);}intmain(){test1();return0;}
案例2:排序结构体数组

按姓名或年龄排序结构体数组,只需编写对应的比较函数。

#include<stdio.h>#include<stdlib.h>#include<string.h>structStu{charname[20];intage;};// 按姓名比较(字典序)intcmp_stu_by_name(constvoid*p1,constvoid*p2){// 结构体指针访问成员用->returnstrcmp(((structStu*)p1)->name,((structStu*)p2)->name);}// 按年龄比较(升序)intcmp_stu_by_age(constvoid*p1,constvoid*p2){return((structStu*)p1)->age-((structStu*)p2)->age;}// 打印结构体数组voidprint_stu(structStuarr[],intsz){inti=0;for(i=0;i<sz;i++){printf("姓名:%s 年龄:%d\n",arr[i].name,arr[i].age);}}voidtest2(){structStuarr[]={{"zhangsan",20},{"lisi",25},{"wangwu",18}};intsz=sizeof(arr)/sizeof(arr[0]);// 按姓名排序qsort(arr,sz,sizeof(arr[0]),cmp_stu_by_name);printf("按姓名排序后:\n");print_stu(arr,sz);// 按年龄排序// qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);// printf("按年龄排序后:\n");// print_stu(arr, sz);}intmain(){test2();return0;}

三、模拟实现qsort:基于冒泡排序的通用改造 🔧

我们可以基于冒泡排序的思想,结合回调函数,模拟实现一个通用的qsort函数,理解其底层逻辑。这一实现思路在笔面试中极容易被考察。

1. 核心改造思路

  1. 参数设计:参考库函数qsort,设计void* base、元素个数、元素大小、比较函数指针四个参数。
  2. 元素比较:借助比较函数指针,调用用户提供的比较逻辑,判断两个元素的大小。
  3. 元素交换:因为元素类型不确定,需按字节交换,设计一个通用的Swap函数。

2. 完整模拟实现代码

#include<stdio.h>#include<string.h>// 通用交换函数:按字节交换两个元素voidSwap(char*buf1,char*buf2,intwidth){inti=0;for(i=0;i<width;i++){chartmp=*buf1;*buf1=*buf2;*buf2=tmp;buf1++;buf2++;}}// 模拟实现qsort(基于冒泡排序)voidbubble_sort(void*base,intsz,intwidth,int(*cmp)(constvoid*p1,constvoid*p2)){inti=0;// 控制冒泡排序的趟数for(i=0;i<sz-1;i++){intflag=1;// 标记是否已有序intj=0;// 控制每一趟的比较次数for(j=0;j<sz-1-i;j++){// 计算第j个和第j+1个元素的地址// 强转为char*,+width就是跳过一个元素的字节数if(cmp((char*)base+j*width,(char*)base+(j+1)*width)>0){// 交换两个元素Swap((char*)base+j*width,(char*)base+(j+1)*width,width);flag=0;}}if(flag==1){break;// 已有序,提前结束}}}// 整型比较函数intcmp_int(constvoid*p1,constvoid*p2){return*(int*)p1-*(int*)p2;}// 打印数组voidprint_arr(intarr[],intsz){inti=0;for(i=0;i<sz;i++){printf("%d ",arr[i]);}printf("\n");}voidtest(){intarr[]={9,7,5,3,1,2,4,6,8,0};intsz=sizeof(arr)/sizeof(arr[0]);bubble_sort(arr,sz,sizeof(arr[0]),cmp_int);printf("排序后:");print_arr(arr,sz);}intmain(){test();return0;}

写在最后 📝

本篇内容聚焦指针在实战和算法中的核心应用,回调函数与qsort的知识点紧密关联笔面试考点。掌握这些内容,你就拥有了应对指针类编程题的重要基础。

下一篇,我们将直击指针经典笔试面试题,从易到难拆解各类高频考题,帮你理清解题思路,轻松应对求职和考试中的指针难关!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/11 22:11:01

Excalidraw绘图支持时间轴模式,展示演进过程

Excalidraw绘图支持时间轴模式&#xff0c;展示演进过程 在一次产品复盘会议上&#xff0c;团队争论不休&#xff1a;三年前的架构决策到底是谁提出的&#xff1f;为什么当时没有引入缓存层&#xff1f;翻遍文档库和会议纪要&#xff0c;依然找不到清晰脉络。这并非个例——当系…

作者头像 李华
网站建设 2026/5/14 4:26:12

Excalidraw结合LLM生成token的智能绘图工作流

Excalidraw结合LLM生成token的智能绘图工作流 在一场紧张的产品评审会上&#xff0c;产品经理刚讲完系统架构设想&#xff0c;工程师便在白板上点击几下&#xff0c;一张清晰的微服务调用图已跃然屏上——这不是科幻场景&#xff0c;而是如今借助Excalidraw与大语言模型&#x…

作者头像 李华
网站建设 2026/5/15 9:43:15

Excalidraw镜像提供健康检查接口,便于运维监控

Excalidraw镜像提供健康检查接口&#xff0c;便于运维监控 在现代云原生架构中&#xff0c;一个看似简单的前端应用能否“稳如磐石”&#xff0c;往往不取决于它的交互多流畅、界面多美观&#xff0c;而在于它是否具备足够的可观测性和自愈能力。Excalidraw 作为一款广受欢迎的…

作者头像 李华
网站建设 2026/5/10 3:08:30

36、异步输入/输出与完成端口技术解析

异步输入/输出与完成端口技术解析 在计算机编程领域,高效的输入/输出操作对于提升程序性能至关重要。本文将深入探讨异步输入/输出(I/O)、可等待计时器、线程池计时器以及 I/O 完成端口等关键技术,为你揭示它们在不同场景下的应用和优势。 异步 I/O 技术 异步 I/O 技术能…

作者头像 李华
网站建设 2026/5/9 9:51:16

2、Windows XP 日常操作与应用指南

Windows XP 日常操作与应用指南 1. 基础文件操作 1.1 创建新文件夹 在 Windows XP 中,若要创建新文件夹来存放即将复制、移动的文件或已安装的程序,可按以下步骤操作: 1. 从 Windows 桌面双击“我的文档”或“我的电脑”文件夹窗口。 2. 点击驱动器,然后找到并点击该驱…

作者头像 李华
网站建设 2026/5/15 19:54:33

Excalidraw与Pabbly Connect集成,企业级自动化就绪

Excalidraw与Pabbly Connect集成&#xff0c;企业级自动化就绪 在今天的研发协作场景中&#xff0c;一个常见的困境是&#xff1a;设计师画完架构图后&#xff0c;还得手动复制链接、发消息提醒、填写工单——明明一张图已经说明了一切&#xff0c;却还要重复“翻译”成各种系统…

作者头像 李华