当前位置: 首页 > news >正文

第15讲:深入理解指针(5)——回调函数与 qsort 深度解析

🧭 第15讲:深入理解指针(5)——回调函数与 qsort 深度解析 🔍🛠️

用函数指针实现“智能排序”与“事件响应”,掌握C语言的高阶编程艺术!


📚 目录

  1. 回调函数:程序的“事件响应机制” 🎯

  2. qsort 使用举例:通用排序引擎 🔢📊

  3. qsort 函数的模拟实现:从零构建通用排序 🔧

  4. 学习收获总结 ✅

回调函数:程序的“事件响应机制” 🎯

🧩 什么是回调函数?

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

📌 核心要点

  • 回调函数不是由函数实现方直接调用。
  • 而是在特定的事件或条件发生时,由另外的一方调用,用于对该事件或条件进行响应。

🎯 为什么需要回调函数? 回顾第13讲中的计算器代码,你会发现红色框中的代码是重复出现的:

  • 输入操作数
  • 输出结果

虽然执行计算的逻辑不同(加、减、乘、除),但输入输出操作是完全冗余的。

有没有办法简化呢?

答案是:有!

因为只有“调用哪个计算函数”这一逻辑有差异,我们可以:

  1. 计算函数的地址以参数形式传递给一个通用处理函数。
  2. 使用函数指针接收这个地址。
  3. 函数指针指向哪个函数,就调用哪个函数。

这,就是回调函数的精髓所在——将“变化的部分”作为参数传递,实现代码复用与逻辑解耦


🔄 回调函数的经典应用场景:计算器优化

❌ 传统写法:冗余重复
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
}

📌 问题:输入输出逻辑重复,仅计算函数不同。


✅ 使用回调函数重构
#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, x, y;printf("输入操作数:");scanf("%d %d", &x, &y);ret = pf(x, y);           // 调用传入的函数printf("ret = %d\n", ret);
}int main() {int input = 1;do {printf("1:add  2:sub  3:mul  4:div  0:exit\n");printf("请选择:");scanf("%d", &input);switch (input) {case 1: calc(add); break;case 2: calc(sub); break;case 3: calc(mul); break;case 4: calc(div); break;case 0: printf("退出程序\n"); break;default: printf("选择错误\n"); break;}} while (input);return 0;
}

🎯 回调函数的优势

优势说明
消除重复代码输入输出逻辑只写一次
提高可维护性新增功能只需添加函数,无需修改主逻辑
实现逻辑解耦calc 不关心具体计算,只负责流程控制
支持运行时绑定可根据条件动态选择调用哪个函数

🌟 应用场景

  • GUI 事件处理(点击、拖拽)
  • 定时器回调
  • 网络请求完成通知
  • 排序比较函数(如 qsort

qsort 使用举例:通用排序引擎 🔢📊

qsort 是 C 标准库中的通用排序函数,使用回调函数实现自定义比较逻辑。

void qsort(void *base, size_t nmemb, size_t size,int (*compar)(const void *, const void *));
参数说明
base待排序数组首地址
nmemb元素个数
size每个元素大小(字节)
compar比较函数指针

📌 比较函数返回值

  • < 0e1 < e2
  • == 0e1 == e2
  • > 0e1 > e2

🔢 排序整型数组

#include <stdio.h>
#include <stdlib.h>// 比较函数:升序排列
int int_cmp(const void *p1, const void *p2) {return (*(int*)p1 - *(int*)p2);
}int main() {int arr[] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 0};int i;qsort(arr, 10, sizeof(int), int_cmp);for (i = 0; i < 10; i++) {printf("%d ", arr[i]);}printf("\n"); // 输出:0 1 2 3 4 5 6 7 8 9return 0;
}

📌 const void*:通用指针类型,可接收任何类型地址。


📊 排序结构体数据

#include <stdio.h>
#include <stdlib.h>
#include <string.h>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;
}// 按名字排序(字典序)
int cmp_stu_by_name(const void *e1, const void *e2) {return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}void test_sort_by_age() {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);for (int i = 0; i < sz; i++) {printf("%s %d\n", s[i].name, s[i].age);}
}void test_sort_by_name() {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);for (int i = 0; i < sz; i++) {printf("%s %d\n", s[i].name, s[i].age);}
}int main() {test_sort_by_age();   // 输出:wangwu 15, zhangsan 20, lisi 30test_sort_by_name();  // 输出:lisi 30, wangwu 15, zhangsan 20return 0;
}

📌 strcmp:标准库函数,用于比较字符串。


qsort 函数的模拟实现:从零构建通用排序 🔧

通过冒泡排序模拟 qsort,深入理解其工作原理。


🛠️ 核心工具函数:_swap

// 通用交换函数:交换任意类型数据
void _swap(void *p1, void *p2, int size) {char tmp;for (int i = 0; i < size; i++) {tmp = *((char*)p1 + i);*((char*)p1 + i) = *((char*)p2 + i);*((char*)p2 + i) = tmp;}
}

📌 void\* 的作用

  • 通用指针,可指向任意类型数据
  • 需强制转换为具体类型(如 char*)进行操作
  • 实现“类型无关”的通用算法

🔁 模拟 qsort:冒泡排序实现

#include <stdio.h>// 比较函数:用于整型
int int_cmp(const void *p1, const void *p2) {return (*(int*)p1 - *(int*)p2);
}// 通用冒泡排序
void bubble_sort(void *base, int count, int size,int (*cmp)(const void *, const void *)) {for (int i = 0; i < count - 1; i++) {for (int j = 0; j < count - i - 1; j++) {// 计算当前元素和下一个元素的地址void *e1 = (char*)base + j * size;void *e2 = (char*)base + (j + 1) * size;// 使用回调函数比较if (cmp(e1, e2) > 0) {_swap(e1, e2, size); // 交换}}}
}int main() {int arr[] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 0};int i;bubble_sort(arr, 10, sizeof(int), int_cmp);for (i = 0; i < 10; i++) {printf("%d ", arr[i]);}printf("\n"); // 输出:0 1 2 3 4 5 6 7 8 9return 0;
}

🎯 模拟实现的关键点

技术点说明
void* base接收任意类型数组地址
(char*)base + j*size正确计算第 j 个元素地址
cmp(...)回调函数决定排序规则
_swap(...)通用交换,支持任意数据类型

扩展性:只需提供新的比较函数,即可排序任何类型数据。


✅ 学习收获总结

技能掌握情况
✅ 理解回调函数的概念与工作原理✔️
✅ 能在项目中使用回调函数消除重复代码✔️
✅ 掌握 qsort 的使用方法✔️
✅ 能编写自定义比较函数(基础/结构体)✔️
✅ 理解 void* 的通用指针作用✔️
✅ 能模拟实现 qsort(冒泡版本)✔️
✅ 掌握通用算法的设计思想✔️

🎯 回调函数是C语言实现“多态”与“高阶函数”的核心机制
你已掌握如何用函数指针编写可复用、可扩展的通用代码,这是迈向高级C程序员的关键一步!💪🔥

💬 需要本讲的 回调函数练习题 / qsort 实战项目 / 动态内存预习资料?欢迎继续提问,我为你准备!

http://www.dtcms.com/a/484043.html

相关文章:

  • 海南网站建设方案阜阳公司网站建设
  • 南山模板网站建设公司建立网站的技术路径
  • OpenCASCADE 放样操作完整指南
  • jsp可以做网站吗什么网站做装修的
  • GBA小游戏下载:500多款GBA小游戏打包下载
  • 南京做网站的客户电话河源城乡规划建设局网站
  • 任务推荐系统的本质:在规则的边界内做最优决策
  • 手机网站 自适应屏幕怎么运营网站
  • 潍坊网站制作软件微信对接网站
  • LangChain4J实战,高效速通
  • 万万州州微微网站网站建建设设做ppt图片网站 知乎
  • 20251014 区间DP总结
  • 商城系统网站模板免费下载浙江平台网站建设公司
  • html5:拖放 / demo / 拖放事件(Drag Events)/ DataTransfer 对象方法
  • 早期小软件与现代大软件的区别与发展问题
  • 图解网络(第二集)
  • 做外贸服装的网站微信如何引流推广精准加人
  • 多态:C++面向对象编程的“灵魂”所在
  • 大连网站快速排名提升深圳互联网公司网站
  • 建设银行广西分行网站做自媒体的网站有哪些
  • 楼市南京做凶宅的网站郑州营销网站建设公司
  • 搭建网站需要备案吗上海网站工作室
  • 学校网站建设计入哪个会计科目类似于wordpress的网站
  • 网站seo其应用买的网站模板怎么做
  • 【GESP】C++五级考试大纲知识点梳理, (3-4) 链表-双向循环链表
  • wordpress打开网站前广告怎样免费建设个人网站
  • 网站logo更换旅游做攻略用什么网站好
  • 天津黑曼巴网站建设无锡网站排名公司
  • 【鸿蒙5.0】Scroll左右滑动
  • 抢购网站源码dz门户 WordPress