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

C语言:指针(4)

1. 回调函数

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

如果将函数指针作为参数传递给另一个函数,另一个函数根据指针来调这个函数,那么被调用的函数就是回调函数。回调函数不是由这个函数的实现方直接调用,而是在特定的条件下由另一方调用的。

例如在指针(3)中,我们将多个函数的地址放在函数指针数组中,根据不同的选择来对输入的x和y执行相应的计算,这里就是采用的就是回调函数的功能。

2. qsort使用举例

2.1 qsort函数

在 C 语言中,qsort是标准库<stdlib.h>提供的快速排序函数,用于对数组进行排序。其函数原型如下:

void qsort(void *base, size_t nmemb, size_t size,int (*compar)(const void *, const void *));

其中:

base是要排序的数组首元素的地址

nmemb是数组中元素的个数

size是每个元素的大小,单位为字节

compar是指比较函数的指针,用来定义排序规则

2.2 利用qsort函数排序整型数据

qsort会根据传递的函数指针所指向的那个函数的返回值来排序,例如两个值a,b进行比较:

当返回值为负时,a就会排在b前面;返回值为正时,a排在b后面;返回值为0时,qsort会将这两个值视为相等,但并不会保证相等元素排序与原始排序相同,例如:原始排序是 a  b,a在前,而qsort排序后可能会是b  a,即相等的元素排序后的相对位置不一定与原始位置相同。

知道这些我们可以利用qsort函数做一个简单的排序,让一个数组中的元素按升序排序:

#include <stdio.h>
#include <stdlib.h>
int i;int cmp(const void* p1,const void* p2)
{return (*(int *)p1 - *(int *)p2);
}
int main()
{int arr[] = {1,4,3,6,8,9,7,2,5,0};int num = sizeof(arr)/ sizeof(arr[0]);int sz = sizeof(arr[0]);qsort(arr,num,sz,cmp);for(i = 0;i < num;i++){printf("%d ",arr[i]);}return 0;
}
0 1 2 3 4 5 6 7 8 9 

我们可以看到输出结果与预期一致,qsort正确得进行了排序。

另外,我们可以看到我们定义的cmp函数中,在进行运算前前面先将指针p1与p2的类型先强制转换为了int *,这是因为void *虽然可以接受各种类型的指针,但是却不能直接使用,必须先进行强制类型转换。这里因为我们希望是升序,所以返回p1减去p2的值。若p1 < p2,返回值为负,p1排在p2前;若p1 > p2,返回值为正,p1排在p2后。如果我们想要实现降序,只需要将cmp函数体内p1与p2的位置互换或者是在计算表达式前加上 - ,改变正负即可。

2.3 使用qsort排序结构数据

现在我们尝试利用qsort函数来对结构数据进行排序,例如:

#include <stdio.h>
#include <stdlib.h>
int i;
struct Stu
{char name[10];int age;
};
int cmp_by_name(const void *p1,const void *p2)
{return strcmp(((struct Stu*)p1)->name,((struct Stu*)p2)->name);
}int main()
{struct Stu s[] = {{"zhangsan",20},{"lisi",23},{"wangwu",19}};int len = sizeof(s)/ sizeof(s[0]);int sz = sizeof(s[0]);qsort(s,len,sz,cmp_by_name);for(i = 0;i < len;i++){printf("%s\n",s[i].name);}return 0;
}
lisi
wangwu
zhangsan

根据打印结果,我们发现,qsort成功按照字母顺序进行了排序。同样的,我们也可以写出cmp_by_age函数,让qsort按照年龄来进行排序。

另外,我在定义函数cmp_by_name时,在函数体内部用 -> 来访问结构中的name,而在打印时使用的是 . 。这是因为,我在定义函数的返回值时,这里的数据是指针类型,只能用->来访问,而在打印时则是直接访问结构,所以要使用 . 来访问结构中数据。因此,如果我们不想使用->,也可以先对指针进行解引用操作然后再使用 . 来访问数据。

3. qsort函数的模拟实现

学习了qsort函数的使用方法后,我们现在尝试自己写代码来模拟实现qsort函数。

例如,我们用冒泡排序的方法法来模拟qsort函数。

那么。根据上面的代码的经验,我们可以得到下面的代码:

#include <stdio.h>int cmp(const void *p1,const void *p2)
{return (*(int*)p1 - *(int*)p2);
}
void swap(void *p1,void *p2,int sz)
{int i;char temp;for(i = 0;i < sz;i++){temp = *((char *)p1 + i);*((char *)p1 + i) = *((char *)p2 + i);*((char *)p2 + i) = temp;}
}void bubble(void* base,int num,int sz,int (*cmp)(const void *p1,const void *p2))
{int i,j;for(i = 0;i < num - 1;i++){for(j = 0;j < num - 1 - i;j++){if(cmp(base + j * sz,base + (j + 1) * sz) < 0){swap(base + j * sz,base + (j + 1) * sz,sz);}}}
}
int main()
{int i;int arr[] = {1,5,3,7,9,4,0,2,6,8};int num = sizeof(arr)/ sizeof(arr[0]);int sz = sizeof(arr[0]);bubble(arr,num,sz,cmp);for(i = 0;i < num;i++){printf("%d ",arr[i]);}return 0;
}
9 8 7 6 5 4 3 2 1 0 

这里主体的逻辑和上面的代码一样,值得一提的是swap这个函数。

在定义这个函数的时候我们是将指针类型强制转换为char * 类型,这样处理的好处是:我们为了模拟qsort函数实现通用设计,在不知道接受的数据类型的前提下,将指针类型强制转换为char *类型,而char *类型指针和 i 相加时只会跳过 i 个字节,这样能够充分交换每一个字节中的数据,防止遗漏,保证了数据的准确性。         

http://www.lryc.cn/news/619995.html

相关文章:

  • 【2025】Datawhale AI夏令营-多模态RAG-Task3笔记-解决方案进阶
  • 蓝蜂网关在雄安新区物联网建设中的关键应用
  • 补环境基础(四) Hook插件
  • Spring Boot项目调用第三方接口的三种方式比较
  • 当img占不满div时,图片居中显示,两侧加当前图片模糊效果
  • 如何记录日常笔记?
  • 【Linux学习|黑马笔记|Day3】root用户、查看权限控制信息、chmod、chown、快捷键、软件安装、systemctl、软连接、日期与时区
  • 语音交互像聊天:声网RTC技术给AI客服加温度
  • 基于 MybatisPlus 将百度天气数据存储至 PostgreSQL 数据库的实践
  • 开发避坑指南(25):MySQL不支持带有limit语句的子查询的解决方案
  • Java研学-RabbitMQ(六)
  • 算法题详细解析 + 代码 + 注释
  • 在 uniapp 里使用 unocss,vue3 + vite 项目
  • 数据结构初阶(12)排序算法—插入排序(插入、希尔)(动图演示)
  • 智驾系统架构解析
  • 常用机器学习公开数据集大全
  • [系统架构设计师]系统架构基础知识(一)
  • [系统架构设计师]信息安全技术基础知识(三)
  • DataOceanAI Dolphin(ffmpeg音频转化教程) 多语言(中国方言)语音识别系统部署与应用指南
  • 最新去水印小程序系统 前端+后端全套源码 多套模版 免授权
  • TF-IDF实战——《红楼梦》文本分析
  • 商品分类拖拽排序设计
  • 用 Qt C++ 从零打通“前端界面 → 后端接口”的数据交互
  • Redis的基础命令
  • 图像分类-动手学计算机视觉10
  • RabbitMQ:Windows版本安装部署
  • 高防CDN和高防IP的各自优势
  • Vue项目生产环境性能优化实战指南
  • 【Java虚拟机】JVM内存模型
  • uniapp跨端性能优化方案