湖南省网站建设_网站建设公司_电商网站_seo优化
2025/12/22 3:49:03 网站建设 项目流程

🏠个人主页:黎雁
🎬作者简介: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的知识点紧密关联笔面试考点。掌握这些内容,你就拥有了应对指针类编程题的重要基础。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询