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

C语言的灵魂—指针(四)

一、回调函数

回调函数就是一个通过指针调用的函数。

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

通过回调函数,我们可以处理代码中冗余的部分,使得代码更加精炼,美观。

如下所示

#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; int x, y; printf("输入操作数: "); scanf("%d %d", &x, &y); ret = pf(x, y); printf("ret = %d\n", ret); } int main() { int input = 1; do { printf("************************\n"); printf(" 1:add\n"); printf(" 2:sub\n"); printf(" 3:mul\n"); printf(" 4:div\n"); printf("************************\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函数,来接收各类计算寒暑,当我们需要调用时,只需要给它传对应的函数即可

二、qsort函数

qsort函数是C语言标准库的快速排序函数,可以帮助你把数据排序,但是他不知道你要怎么比大小,所以需要你自己写一个“比较函数”

它不同于我们已知的冒泡排序函数,它可排列的数据类型不仅仅局限于整型,还包含字符型,结构体类型等等,全由你自己提供的函数决定

void qsort(void* base , size_t mem , size_t size , int(* compare)(const void* , const void*))

第一个参数指向的是 要排序数组的起始地址

第二个参数是 数组的元素个数

第三个参数是是 一个元素所占的字节数

第四个参数是是 你自己提供的比较函数(这个比较函数应该返回一个整型)

使用示例

排列整型

#include <stdio.h> #include <stdlib.h> // qsort 函数需要包含这个头文件 // qsort函数的使用者得实现一个比较函数 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 = 0; qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp); for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }

注意,为了让这个比较函数可以接受任意类型的变量,我们需要把它的参数设置成void*类型,但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 test2()//排列年龄 { 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); } void test3()//排列名字 { 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); } int main() { test2(); test3(); return 0; }

现在我们尝试模拟实现它

我们可以借鉴下冒泡排序的思路,在它的基础上进行改造,从而使其不仅仅只能排列整型,还可以排列结构体类型

#include <string.h> struct stu { int age; char name[20]; }; int age_cmp(const void* p1 , const void* p2) { return (*(struct stu*)p1).age - (*(struct stu*)p2).age; } int name_cmp(const void* p1, const void* p2) { return strcmp((*(struct stu*)p1).name , (*(struct stu*)p2).name); } void my_swap(void* p1, void* p2, int size) { int i = 0; for (i = 0; i < size; i++) { char tmp = *((char*)p1 + i); *((char*)p1 + i) = *((char*)p2 + i); *((char*)p2 + i) = tmp; } } void my_qsort(void* base, int num, int sz , int(* cmp)(void* , void*) ) { int i = 0; for (i = 0 ; i < num-1 ; i++) { int j = 0; for (j = 0 ; j < num - 1 - i ; j++) { if(cmp((char*)base + j*sz , (char*)base + (j+1)*sz) > 0) my_swap((char*)base + j * sz, (char*)base + (j + 1) * sz,sz); } } } int main() { struct stu a = { 25,"zhangsan" }; struct stu b = { 27,"lisi" }; struct stu c = { 34,"wangwu" }; struct stu arr[] = {a , b, c}; int i = 0; my_qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]), age_cmp); for(i = 0 ; i < (sizeof(arr) / sizeof(arr[0])) ; i++) { printf("%d\n", arr[i].age); } my_qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]), name_cmp); for (i = 0; i < (sizeof(arr) / sizeof(arr[0])); i++) { printf("%s\n", arr[i].name); } return 0; }

这里的难点在于,在冒泡函数中,我们可以使用数组的形式对数据进行比较大小和交换顺序,但是void*类型的数据不支持数组下标运算。

因此,我们需要转换思路,返璞归真。

数组运算的本质就是地址加偏移量,比如arr[4]就是 *(arr + 4)

我们既然有数组的地址,单个元素的大小,就可以模仿这种形式,用地址 + 偏移量来表示元素

因此就有了(char*)base + j*sz这个形式。

先将指针强制类型转化成char*类型,这样字节的最小偏移单位就是1了,再用元素地址加一个元素的偏移量的形式找到相邻下个元素的地址,从而达到向cmp函数传参的目的。

http://www.jsqmd.com/news/450125/

相关文章:

  • 火星环境代码测试规范
  • 这3个原因让我果断选择了网安,转行网络安全经验分享!
  • win10部署openclaw以及配置局域网访问
  • 模糊数乘法运算(与门逻辑)
  • DC-DC移相全桥MATLAB仿真 DC- DC移相全桥电路 移相全桥DC-DC变换器matlab/simulink仿真,功率管采用mosfet,副边接整流电路。 采用PWM控制
  • 电力电子技术详解:交交变频电路Matlab模型与单相、三相交流调压电路研究,专业波形图解析,加...
  • 毕业论文神器 9个AI论文工具深度测评:本科生科研写作必备指南
  • 编译原理--文法定义(哈工大)
  • MATLAB常见错误与高效调试技巧
  • 分享浙江森谷声学技术有限公司情况,森谷声学反馈好不好呢 - 工业设备
  • Trae轻松安装openclaw的教程-附带免费token
  • 题解:AT_abc441_e [ABC441E] A > B substring
  • 2026年有实力的财税合规公司哪家好,华光讯服务物流运输中小企业 - 工业推荐榜
  • 2026年中国留学生求职机构权威榜单发布:五大品牌服务实力深度排位赛 - 品牌推荐
  • 佛山深信服EDR杀毒免费上门服务
  • ARP欺骗一篇文章讲透:原理、攻击与防御全解析
  • 2026软著版本号怎么填?V1.0还是1.0?如何保证材料全局一致不补正
  • java字面量
  • 基于西门子S7-200 PLC的智能照明控制系统设计与实现:包含电路图、IO表、源程序及单机组...
  • 2026恒压变频供水设备市场,这些厂家口碑佳,无负压供水设备/消防泵/污水提升设备,恒压变频供水设备实力厂家哪个好 - 品牌推荐师
  • 二手观光车性价比高的企业
  • 【运维实操】浅谈CDN在网站运行中的核心价值,360CDN实操体验分享
  • 收藏!2026大模型转行/入门指南:普通人落地AI的实战路线(避开90%新手坑)
  • 传统分块已死?Agentic Chunking拯救语义断裂,实测RAG准确率飙升40%!
  • 2026年和你一起品味浙江静音房设计来图定制企业哪家好 - 工业品网
  • 华为 S5700 三层交换 VLAN 互通与 ACL 隔离实战笔记
  • hot100 62.不同路径
  • Flutter 三方库 coingecko_api 的鸿蒙化适配指南 - 掌控货币行情资产、精密金融治理实战、鸿蒙级行情专家
  • AiPPT接口文件PHP版本全,智能生成PPT文件并下载
  • 不需要 RAG!在 30 分钟内构建一个问答 AI 代理-万字长文,慎点!