linux c 语言回调函数学习

动机

最近在看 IO多路复用,包括 select() poll () epoll() 的原理以及libevent, 对里面提及的回调机制 比较头大,特写此文用例记录学习笔记。

什么是回调函数

网上看到的最多的一句话便是:回调函数 就是 函数指针的一种用法,将回调函数指针作为另一个函数的参数

为什么要用回调函数

解耦
以下图为例:
在这里插入图片描述

  • 需求:我们知道排序算法有很多种,包括冒泡,选择,归并等等。现在我们想对某一个数组实现一个排序功能,需要对比不同算法的复杂度。
  • 针对该需求,上图可以抽象为如下代码:
#include<stdio.h>
#include<lib.h> // 包含Library函数的头文件
 
int Callback() // Callback Function 用于定义实现不同的排序算法
{ 
    冒泡;
    选择;
    归并;
    ...  
    return 0;
}
int main() // Main program
{
	%给定待排序的数组
	int a[3] ={2,1,3}; 
    %% 库函数进行回调 (即:选择不同的排序算法)
    Library(Callback);
    return 0;
}
  1. 那么,可以看到 ,我们只需要向Library函数中 传入不同的参数,就可以实现不同的排序功能。
  2. 另外还可以看到,main和callback函数实现在一个文件,Library函数实现在另一个文件。现实工作中,这个Library函数怎么编写我们是看不见的,只能拿到Library函数提供给我们的一个接口,那我们只要给Library函数传入不同的回调函数,就可以实现不同的功能了

函数指针

前面提过,要把一个函数A作为另一个函数B的参数,很直接的一种方式就是向B 传入A的指针。
现对函数指针做个简单介绍:

函数指针也是一种指针,只是它指向的不是整型,字符型而是函数。在C中,每个函数在编译后都是存储在内存中,并且每个函数都有一个入口地址,根据这个地址,我们便可以访问并使用这个函数。函数指针就是通过指向这个函数的入口,从而调用这个函数。

函数指针的定义:

和其他指针的定义略有不同:

%整型指针
int *p = NULL;
%函数指针
/* 方法1 */
void (*p_func)(int, int, float) = NULL;
/* 方法2 */
typedef void (*tp_func)(int, int, float);
tp_func p_func = NULL;

上例定义了一个指向返回值为 void 类型

函数指针的赋值
void (*p_func)(int, int, float) = NULL;
p_func = &func1;
p_func = func2;

上面两种方法都是合法的,对于第二种方法,编译器会隐式地将 func_2 由 void ()(int, int, float) 类型转换成 void (*)(int, int, float) 类型,

使用函数指针调用函数
/* 方法1 */
int val1 = p_func(1,2,3.0);
/* 方法2 */
int val2 = (*p_func)(1,2,3.0);
将函数指针作为参数传给函数
/* func3 将函数指针 p_func 作为其形参 */
void func3(int a, int b, float c, void (*p_func)(int, int, float))
{
    (*p_func)(a, b, c);
}

/* func4 调用函数func3 */
void func4()
{
    func3(1, 2, 3.0, func_1);
    /* 或者 func3(1, 2, 3.0, &func_1); */
}
函数指针作为函数返回类型
void (* func5(int, int, float ))(int, int)
{
    ...
}
函数指针数组

如下代码定义了一个元素个数为5,类型是 void (*)(int, int, float) 的函数指针数组

/* 方法1 */
void (*func_array_1[5])(int, int, float);

/* 方法2 */
typedef void (*p_func_array)(int, int, float);
p_func_array func_array_2[5];

回调函数简单例子

#include <stdio.h>
#include <stdlib.h>

/****************************************
 * 加减乘除函数
 ***************************************/
float ADD(float a, float b) 
{
    return a + b;
}

float SUB(float a, float b) 
{
    return a - b;
}

float MUL(float a, float b) 
{
    return a * b;
}

float DIV(float a, float b) 
{
    return a / b;
}

/****************************************
 * 函数指针结构体
 ***************************************/
typedef struct _OP {
    float (*p_add)(float, float); 
    float (*p_sub)(float, float); 
    float (*p_mul)(float, float); 
    float (*p_div)(float, float); 
} OP; 


/****************************************
 * 初始化函数指针
 ***************************************/
void init_op(OP *op)
{
    op->p_add = ADD;
    op->p_sub = SUB;
    op->p_mul = &MUL;
    op->p_div = &DIV;
}


/****************************************
 * 库函数
 ***************************************/
float add_sub_mul_div(float a, float b, float (*op_func)(float, float))
{
    return (*op_func)(a, b);
}

int main()
{
    OP *op = (OP *)malloc(sizeof(OP)); 
    init_op(op);

    /* 调用回调函数 */ 
    printf("ADD = %f, SUB = %f, MUL = %f, DIV = %f\n", 
            add_sub_mul_div(1.3, 2.2, ADD), 
            add_sub_mul_div(1.3, 2.2, SUB), 
            add_sub_mul_div(1.3, 2.2, MUL), 
            add_sub_mul_div(1.3, 2.2, DIV));
    free(op);
}

参考:

1
2:https://segmentfault.com/a/1190000008293902

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python 正则表达式的一些介绍和使用方法说明(数字、字母和数字、电子邮件地址、网址、电话号码(简单)、IPv4 )

## 正则表达式的概念和用途 正则表达式&#xff08;Regular Expression&#xff0c;简称Regex&#xff09;是对字符串操作的一种逻辑公式&#xff0c;由一些事先定义好的特定字符以及这些特定字符的组合所构成。这些特定字符及其组合被用来描述在搜索文本时要匹配的一个或多个…

DreamClear:字节跳动开源了高性能图像修复技术,中科院加持,商业免费使用

哇&#xff0c;字节跳动开源了DreamClear项目&#xff0c;采用的是Apache-2.0开源协议&#xff0c;可以商用&#xff0c;并且用户可以自由地使用、复制、修改和分发该软件&#xff0c;甚至可以用于私有项目中。这对于开发者和企业来说是个好消息&#xff0c;因为它们可以利用这…

Flutter:android studio无法运行到模拟机的问题

提示如下错误信息&#xff1a; Entrypoint is not a Dart filenot applicable for the "main.dart" configurat点击运行按钮提示让填写以下信息 或者出现无法选择模拟机的情况 发下下列问题&#xff1a; 无法运行的项目默认根目录地址&#xff1a; 可以正常运行…

FromData格式提交接口时入参被转成JSON格式问题

本地上传文件后通过事件提交文件&#xff0c;一般先通过前端组件生成文本流&#xff0c;在通过接口提交文本流&#xff0c;提交文本流一般使用FormData的入参形式传入&#xff0c;接口请求头也默认"Content-Type": “multipart/form-data”&#xff0c;但是某些场景统…

<AI 学习> 下载 Stable Diffusions via Windows OS

注意&#xff1a; 不能使用 网络路径 不再支持 HTTPS 登录&#xff0c;需要 Token 1. 获得合法的授权 Stability AI License — Stability AI 上面的链接打开&#xff0c;去申请 许可 2. 拥有 HuggingFace 账号 注册&#xff1a;https://huggingface.co/ 3. 配置 Tok…

MySQL缓存使用率超过80%的解决方法

MySQL缓存使用率超过80%的解决方法 一、识别缓存使用率过高的问题1.1 使用SHOW GLOBAL STATUS命令监控1.2 监控其他相关指标二、分析缓存使用率过高的原因2.1 数据量增长2.2 查询模式变化2.3 配置不当三、解决缓存使用率过高的方法3.1 调整Buffer Pool大小3.1.1 计算合理的Buff…

39.安卓逆向-壳-smali语法3(方法)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;图灵Python学院 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要盲目相信。 工…

《FreeRTOS任务基础知识以及任务创建相关函数》

目录 1.FreeRTOS多任务系统与传统单片机单任务系统的区别 2.FreeRTOS中的任务&#xff08;Task&#xff09;介绍 2.1 任务特性 2.2 FreeRTOS中的任务状态 2.3 FreeRTOS中的任务优先级 2.4 在任务函数中退出 2.5 任务控制块和任务堆栈 2.5.1 任务控制块 2.5.2 任务堆栈…

RHCE的学习(18)

第二章 变量和引用 深入认识变量 在程序设计语言中&#xff0c;变量是一个非常重要的概念。也是初学者在进行Shell程序设计之前必须掌握的一个非常基础的概念。只有理解变量的使用方法&#xff0c;才能设计出良好的程序。本节将介绍Shell中变量的相关知识。 什么是变量 顾名思义…

AG32 FPGA部分简单开发

环境 Quartus 13.0&#xff08;Quartus 不能使用Lite 版本&#xff0c;需要使用Full 版本&#xff09;AGM SDKSupra&#xff08;快捷方式在SDK目录下&#xff0c;具体路径为AgRV_pio\packages\tool-agrv_logic\bin&#xff09; FPGA编程 在AG32芯片中&#xff0c;拥有异构双…

__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ is not explicitly defined

VUE_PROD_HYDRATION_MISMATCH_DETAILS 未明确定义。您正在运行 Vue 的 esm-bundler 构建&#xff0c;它期望这些编译时功能标志通过捆绑器配置全局注入&#xff0c;以便在生产捆绑包中获得更好的tree-shaking优化。 Vue.js应用程序正在使用ESM&#xff08;ECMAScript模块&#…

Spring——事务

事务 JdbcTemplate 简介 Spring框架对JDBC进行封装&#xff0c;使用JdbcTemplate方便实现对数据库操作 准备工作 ①搭建子模块 搭建子模块&#xff1a;spring-jdbc-tx ②加入依赖 <dependencies><!--spring jdbc Spring 持久化层支持jar包--><dependenc…

集合类源码浅析のJDK1.8ConcurrentHashMap(下篇)

文章目录 前言一、分段扩容1、addCount2、transfer3、helpTransfer 二、查询二、删除总结 前言 主要记录ConcurrentHashMap&#xff08;笔记中简称CHM&#xff09;的查询&#xff0c;删除&#xff0c;以及扩容方法的关键源码分析。 一、分段扩容 1、addCount 扩容的逻辑主要在…

H5页面多个视频如何只同时播放一个?

目录 背景1. 首先介绍下 muted 属性2. 监听播放和暂停操作3. 视频播放完毕后返回桌面&#xff0c;再进入H5页面发现视频封面丢失置灰解决思路&#xff1a; 背景 页面模块同时有个四个视频模块&#xff0c;发现可以同时播放四个视频&#xff0c;但是理想的是每次只播放一个。 …

D69【 python 接口自动化学习】- python 基础之数据库

day69 Python 执行 SQL 语句 学习日期&#xff1a;20241115 学习目标&#xff1a; MySQL 数据库&#xfe63;- Python连接redis 学习笔记&#xff1a; redis数据库的用途 使用Python访问redis数据库 使用Python对redis数据库进行读写操作 总结 1. redis是一款高性能的键…

jmeter常用配置元件介绍总结之逻辑控制器

系列文章目录 安装jmeter jmeter常用配置元件介绍总结之逻辑控制器 逻辑控制器1.IF控制器2.事务控制器3.循环控制器4.While控制器5.ForEach控制器6.Include控制器7.Runtime控制器8.临界部分控制器9.交替控制器10.仅一次控制器11.简单控制器12.随机控制器13.随机顺序控制器14.吞…

21.<基于Spring图书管理系统②(图书列表+删除图书+更改图书)(非强制登录版本完结)>

PS&#xff1a; 开闭原则 定义和背景‌ ‌开闭原则&#xff08;Open-Closed Principle, OCP&#xff09;‌&#xff0c;也称为开放封闭原则&#xff0c;是面向对象设计中的一个基本原则。该原则强调软件中的模块、类或函数应该对扩展开放&#xff0c;对修改封闭。这意味着一个软…

springboot实现简单的数据查询接口(无实体类)

目录 前言&#xff1a;springboot整体架构 1、ZjGxbMapper.xml 2、ZjGxbMapper.java 3、ZjGxbService.java 4、ZjGxbController.java 5、调用接口测试数据是否正确 6、打包放到服务器即可 前言&#xff1a;springboot整体架构 文件架构&#xff0c;主要编写框选的这几类…

【已解决】 Tomcat10.1.x使用JSTL标签库

IDEA创建Java EE项目&#xff0c;使用Spring Spring MVC MyBatis框架&#xff0c;使用maven管理依赖。项目当前的环境是&#xff1a; Tomat 10.1.28Maven 3.6.3JDK 17 项目的功能&#xff1a;读取数据库的report表中的数据&#xff0c;返回一个List集合对象reportList在JSP…

权限相关知识

1.Linux权限的概念 在说Linux权限的概念之前我来问大家一个问题&#xff0c;你们觉得什么是权限&#xff1f; 权限平时的体现呢&#xff0c;就比如不是校长的亲戚就不能逛办公室&#xff0c;没充会员的爱奇艺看不了VIP影视剧&#xff0c;没成会员的的蛋糕店拿不到会员价等等等…