动机
最近在看 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;
}
- 那么,可以看到 ,我们只需要向Library函数中 传入不同的参数,就可以实现不同的排序功能。
- 另外还可以看到,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