深入理解指针与回调函数:从基础到实践

引言

在C语言中,指针和回调函数是两个非常重要的概念。指针为我们提供了直接操作内存的能力,而回调函数则为我们提供了一种灵活的编程方式,使得我们可以将函数作为参数传递给其他函数,从而实现更加模块化和可复用的代码。本文将深入探讨指针和回调函数的概念,并通过实际的代码示例来展示它们的应用。

1. 回调函数是什么?


1.1 回调函数的定义


回调函数(Callback Function)是一种通过函数指针调用的函数。简单来说,回调函数是一个在特定事件或条件发生时被调用的函数。回调函数不是由该函数的实现方直接调用,而是通过函数指针传递给另一个函数,由该函数在适当的时机调用。

1.2 回调函数的作用


回调函数的主要作用是解耦代码。通过将函数作为参数传递,我们可以将代码的逻辑分离,使得代码更加模块化和可复用。例如,在一个计算器程序中,我们可以将加法、减法、乘法、除法等操作封装成不同的函数,然后通过回调函数的方式将这些函数传递给一个统一的处理函数,从而避免重复的代码。

1.3 回调函数的示例


让我们通过一个简单的例子来理解回调函数的使用。假设我们有一个计算器程序,用户可以选择不同的操作(加法、减法、乘法、除法),程序会根据用户的选择执行相应的操作。

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



在这个代码中,我们使用了switch语句来根据用户的选择调用不同的函数。虽然这个代码可以正常工作,但是它存在一个问题:每次添加一个新的操作时,我们都需要修改switch语句,这会导致代码的冗余和不易维护。

1.3.2 使用回调函数改造后


为了简化代码,我们可以使用回调函数的方式将不同的操作函数作为参数传递给一个统一的处理函数。这样,当我们添加新的操作时,只需要添加新的函数,而不需要修改switch语句。

#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 calculate(int (*func)(int, int)) {int x, y;printf("输入操作数:");scanf("%d %d", &x, &y);int ret = func(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(" 0:exit \n");printf("**********\n");printf("请选择:");scanf("%d", &input);switch (input) {case 1:calculate(add);break;case 2:calculate(sub);break;case 3:calculate(mul);break;case 4:calculate(div);break;case 0:printf("退出程序\n");break;default:printf("选择错误\n");break;}} while (input);return 0;
}


在这个改造后的代码中,我们定义了一个calculate函数,它接受一个函数指针作为参数。这个函数指针指向一个接受两个int参数并返回int的函数。在main函数中,我们根据用户的选择将不同的操作函数传递给calculate函数,从而实现了代码的简化和模块化。

2. qsort使用举例


2.1 qsort函数简介


qsort是C标准库中的一个函数,用于对数组进行排序。它的原型如下:

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


base:指向要排序的数组的第一个元素的指针。

nmemb:数组中元素的个数。

size:数组中每个元素的大小(以字节为单位)。

compar:指向比较函数的指针。这个函数用于比较两个元素的大小。

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


让我们通过一个简单的例子来演示如何使用qsort函数对整型数组进行排序。

#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 = 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;
}


在这个例子中,我们定义了一个int_cmp函数,用于比较两个整型数的大小。然后我们使用qsort函数对数组arr进行排序。qsort函数会根据int_cmp函数的返回值来决定元素的顺序。

2.3 使用qsort排序结构数据


qsort函数不仅可以用于排序基本数据类型,还可以用于排序结构体数据。让我们通过一个例子来演示如何使用qsort函数对结构体数组进行排序。

#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);for (int i = 0; i < sz; i++) {printf("%s %d\n", s[i].name, s[i].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);for (int i = 0; i < sz; i++) {printf("%s %d\n", s[i].name, s[i].age);}
}int main() {test2();test3();return 0;
}


在这个例子中,我们定义了一个Stu结构体,包含学生的姓名和年龄。我们定义了两个比较函数cmp_stu_by_age和cmp_stu_by_name,分别用于按照年龄和姓名对学生进行排序。然后我们使用qsort函数对结构体数组进行排序,并输出排序后的结果。

3. qsort函数的模拟实现

3.1 冒泡排序简介


冒泡排序是一种简单的排序算法。它重复地遍历要排序的数组,比较相邻的两个元素,如果它们的顺序错误就交换它们。遍历数组的工作会重复进行,直到没有需要交换的元素为止。

3.2 使用回调函数模拟实现qsort


我们可以使用冒泡排序的思想来模拟实现qsort函数。为了支持不同类型的数组,我们需要使用void *指针和回调函数。

#include <stdio.h>int int_cmp(const void *p1, const void *p2) {return (*(int *)p1 - *(int *)p2);
}void _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 bubble(void *base, int count, int size, int (*cmp)(void *, void *)) {int i = 0;int j = 0;for (i = 0; i < count - 1; i++) {for (j = 0; j < count - i - 1; j++) {if (cmp((char *)base + j * size, (char *)base + (j + 1) * size) > 0) {_swap((char *)base + j * size, (char *)base + (j + 1) * size, size);}}}
}int main() {int arr[] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 0};int i = 0;bubble(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;
}


在这个代码中,我们定义了一个bubble函数,它接受一个void *指针作为数组的基地址,数组的元素个数,每个元素的大小,以及一个比较函数。bubble函数使用冒泡排序算法对数组进行排序。为了支持不同类型的数组,我们使用void *指针和_swap函数来交换元素。

4. 总结


通过本文的学习,我们深入理解了指针和回调函数的概念,并通过实际的代码示例展示了它们的应用。回调函数为我们提供了一种灵活的编程方式,使得我们可以将函数作为参数传递给其他函数,从而实现更加模块化和可复用的代码。qsort函数是C标准库中的一个强大的排序函数,它通过回调函数的方式支持对不同类型的数据进行排序。我们还通过模拟实现qsort函数,进一步加深了对指针和回调函数的理解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/27513.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot全局异常处理:“危机公关”团队

目录 一、全局异常处理的作用二、Spring Boot 实现全局异常处理&#xff08;附上代码实例&#xff09;三、总结&#xff1a; &#x1f31f;我的其他文章也讲解的比较有趣&#x1f601;&#xff0c;如果喜欢博主的讲解方式&#xff0c;可以多多支持一下&#xff0c;感谢&#x1…

数据集/API 笔记 新加坡相对湿度数据

data.gov.sg 数据时间范围&#xff1a;2016年11月 - 2025年3月 新加坡国家环境局 (NEA) 每分钟记录各个气象站的相对湿度数据&#xff0c;每五分钟更新一次。 数据由自动气象仪器采集&#xff0c;并在生成后立即自动发布。由于技术问题&#xff0c;数据可能会有缺失的情况。…

【前端基础】2、HTML的元素(基础说明)

一、元素概述 HTML本质是元素组成。 元素是网页的一部分。一个元素可以包含一个数据项&#xff0c;或者一块文本&#xff0c;或者一个图片&#xff0c;或者什么都不包含。 二、元素的组成 开始标签&#xff0c;结束标签&#xff0c;内容&#xff0c;组成一个完整元素。 三…

基于深度学习的网络摄像头图像实时分类实践:从理论到完整实现

引言&#xff1a;智能视觉感知的新可能 在人工智能技术蓬勃发展的今天&#xff0c;实时图像分类作为计算机视觉的基础任务之一&#xff0c;正在深刻改变着我们的生活。从智能手机的人脸解锁到无人超市的自动结算系统&#xff0c;从工业质检的缺陷检测到医疗影像的辅助诊断&…

Linux-计算机网络.udp

1.收发函数: read&#xff08;&#xff09;/write () ///通用文件读写&#xff0c;可以操作套接字。 recv(,0) /send(,0) ///TCP 常用套机字读写 recvfrom()/sendto() ///UDP 常用套接字读写 ssize_t recv(int sockfd, void *buf, size_t len, …

如何安装VM虚拟机

安装 VMware 附官方下载链接&#xff08;VM 17 pro&#xff09;&#xff1a;https://download3.vmware.com/software/WKST-1701-WIN/VMware-workstation-full-17.0.1-21139696.exe 打开下载好的VMware Workstation 17 Pro安装包&#xff1b; 点击下一步&#xff1b; 勾选我接…

js的简单介绍

一.javascript&#xff08;是什么&#xff09; 是一种运行在客户端(浏览器)的编程语言&#xff0c;实现人机交互效果 作用 网页特效&#xff08;监听客户的一些行为让网页做出对应的反馈&#xff09;表单验证(针对表格数据的合法性进行判断)数据交互(获取后台的数据&#xf…

绕过 RAG 实时检索瓶颈,缓存增强生成(CAG)如何助力性能突破?

编者按&#xff1a; 你是否曾经遇到过这样的困扰&#xff1a;在开发基于 RAG 的应用时&#xff0c;实时检索的延迟让用户体验大打折扣&#xff1f;或者在处理复杂查询时&#xff0c;检索结果的不准确导致回答质量不尽如人意&#xff1f; 在当前大语言模型应用大规模落地的背景下…

【Java SE】面向对象编程(基础)

面向对象编程&#xff08;基础&#xff09; 目录 1.类与对象的关系 2.对象在内存中的存在形式 2.2 注意事项&#xff08;1&#xff09; 2.3 注意事项&#xff08;2&#xff09; 3.对象的创建方式 4.变量 4.1 成员变量 4.1.1 语法格式 4.1.2 说明 4.2 局部变量 4.2.1…

excel 斜向拆分单元格

右键-合并单元格 右键-设置单元格格式-边框 在设置好分割线后&#xff0c;你可以开始输入文字。 需要注意的是&#xff0c;文字并不会自动分成上下两行。 为了达到你期望的效果&#xff0c;你可以通过 同过左对齐、上对齐 空格键或使用【AltEnter】组合键来调整单元格中内容的…

LeetCode 21. 合并两个有序链表(Python)

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例 2&#xff1a; 输入&#xff1a;l1 [], l2 [] 输出&#xff1a;[] 示例 3&#xff1a; 输…

Linux下安装VS Code

Centos 7 https://blog.csdn.net/weixin_63790642/article/details/132927888 安装存储库 sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc密钥 sudo sh -c echo -e "[code]\nnameVisual Studio Code\nbaseurlhttps://packages.microsoft.com/yum…

【软考-架构】2.1、操作系统概述-进程管理-同步互斥

✨资料&文章更新✨ GitHub地址&#xff1a;https://github.com/tyronczt/system_architect 文章目录 操作系统知识操作系统概述进程组成和状态&#x1f4af;考试真题前趋图进程资源图&#x1f4af;考试真题问题1问题2 ✨【重点】进程同步与互斥✨&#x1f4af;考试真题问题…

养老小程序方案详解居家养老小程序系统

养老小程序&#xff0c;上门居家养老小程序&#xff0c;用户端护工端小程序&#xff0c;管理后台。php开发语言&#xff0c;可源码搭建&#xff0c;二次开发或者定制开发。 一 用户端&#xff1a;小程序 核心功能模块&#xff1a;用户完善个人健康档案&#xff0c;在线选择服…

基于NI USRP 硬件的下一代O-RAN研究测试台​

目录 基于NI SDR硬件的下一代O-RAN研究测试台​挑战&#xff1a;解决方案&#xff1a; 基于NI SDR硬件的下一代O-RAN研究测试台​ “OAIC提供了一个开放平台&#xff08;包括软件架构、库和工具集&#xff09;&#xff0c;用于对基于AI的无线接入网(RAN)控制器进行原型开发和测…

磁盘空间不足|如何安全清理以释放磁盘空间(开源+节流)

背景&#xff1a; 最近往数据库里存的东西有点多&#xff0c;磁盘不够用 查看磁盘使用情况 df -h /dev/sda5&#xff08;根目录 /&#xff09; 已使用 92% 咱们来开源节流 目录 背景&#xff1a; 一、开源 二、节流 1.查找 大于 500MB 的文件&#xff1a; 1. Snap 缓存…

WP 高级摘要插件:助力 WordPress 文章摘要精准自定义显示

wordpress插件介绍 “WP高级摘要插件”功能丰富&#xff0c;它允许用户在WordPress后台自定义文章摘要。 可设置摘要长度&#xff0c;灵活调整展示字数&#xff1b;设定摘要最后的显示字符&#xff0c; 如常用的省略号等以提示内容未完整展示&#xff1b;指定允许在摘要中显示…

健康医疗大数据——医疗影像

一、 项目概述 1.1 项目概述 1.2 项目框架 1.3 项目环境 1.4 项目需求 二、项目调试与运行 2.1需求分析 2.2具体实现 三、项目总结 项目概述 项目概述 本项目旨在应用大数据技术于医疗影像领域&#xff0c;通过实训培养团队成员对医疗大数据处理和分析的实际…

C# OnnxRuntime部署DAMO-YOLO人头检测

目录 说明 效果 模型信息 项目 代码 下载 参考 说明 效果 模型信息 Model Properties ------------------------- --------------------------------------------------------------- Inputs ------------------------- name&#xff1a;input tensor&#xff1a;Floa…

VPC2-多域攻击-tomcat渗透-通达oa-域控提权-密码喷射-委派攻击-数据库提权

下载链接: https://pan.baidu.com/s/1nUYj6G9ouj6BcumDgoDaGg 提取码: ejbn jishu域 windows 2008 tomcat渗透 访问发现tomcat 点击manage app 尝试弱口令进入,发现tomcat/tomcat成功进入 用哥斯拉生成后门 然后建立一个文件夹&#xff0c;把它放进去&#xff0c;把它改名…