十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
引言:回调函数是 C 语言中函数指针
的一种用法,在一个函数A中通过函数指针调用另一个函数B的过程称为回调(callback)
,通过函数指针被调用的函数B就是回调函数
。回调让函数 B 的代码更加泛化和抽象,增强了程序的灵活性和可维护性。linux 底层驱动和 lwip 协议栈内部都大量使用了回调函数,回调函数实在是 编程人员不得不知的编程技巧。
在下述程序中函数 test2_cal()
中调用函数指针 s_cal 指定的函数
执行数值的计算。则s_cal 指定的那些函数
就可以看作一个回调函数。
typedef int (*my_calculate_t)(int a, int b);
static int cal_sum(int a, int b)
{printf("now is sum\r\n");
return a + b;
}
static int cal_sub(int a, int b)
{printf("now is sub\r\n");
return a - b;
}
static int cal_mul(int a, int b)
{printf("now is mul\r\n");
return a * b;
}
static my_calculate_t s_cal = cal_sum;
static int test2_cal (int a, int b)
{int result = 0;
if(s_cal) {result = s_cal(a ,b);
printf("result=%d\r\n", result);
}
return result;
}
void app_main(void)
{printf("init done\r\n");
int m = 10, n = 1, ret;
ret = test2_cal(m, n);
}
上述程序中 s_cal 的值为cal_sum()
,函数 cal_sum()
就是一个回调函数;即当前要执行的运算为cal_sum
定义的加法运算。
当前程序的输出结果:
init done
now is sum
result=11
也可以改变 s_cal 的值为cal_sub
,cal_mul
,它们分别对应减法、乘法运算。读者可自行赋值进行测试。
小结:从上述测试,我们不难理解,仅仅通过更改一个s_cal
函数指针的值分别指向cal_sum
,cal_sub
,cal_mul
就可以实现整个程序运行不同的运算。运算的接口被统一为test2_cal()
,它具备了执行多种运算的功能(通过更改s_cal
函数指针的值指定其功能)。另外,从该示例中,对于回调函数,我们还认识到它往往具有一个外壳
,如本例中的test2_cal()
就是外壳,和一个核心
,即函数指针,如本例中的s_cal
函数指针。
回调对于编写库文件有很大的好处,比如我们要实现一个加法,但加法分很多种:整数的加法、字符串的加法、指针的加法等等。我们可以定义一个统一的add()
,并在add()
中定义一个函数指针s_add
,通过更改s_add
所指的函数,来适应多种数据类型的加法。这在C++ 中被称为“多态”,即根据输入的数据类型,调用符合该数据类型运算的函数。
同样的,在编写驱动程序时,由于不同的设备具备不同的特性,初始化时的内容可能不一样。使用回调函数,可以通过改变对应的函数指针的值,来指向不同的设备的初始化函数,实现能够兼容许多设备的驱动程序。
如上所示,回调函数是通过函数指针来调用的。因此想改变回调函数的功能,就是研究如何改变函数指针的值。主要有以下三种方法:
1)编译时直接赋值如上一节所示的示例,通过对函数指针s_cal
赋值,可以改变函数test2_cal()
实际运行的计算。这在编译时就知道要将函数指针s_cal
赋予的值的情况下,可以使用。若在编译的时候不知道具体要将s_cal
赋予什么值,或者需要程序运行时动态地改变s_cal
的值,直接赋值的方法无法正常使用。
运行时,可以通过其他函数对函数指针进行赋值,在程序运行的时候,动态地改变函数的行为。
示例:
typedef int (*my_calculate_t)(int a, int b);
static int cal_sum(int a, int b)
{printf("now is sum\r\n");
return a + b;
}
static int cal_sub(int a, int b)
{printf("now is sub\r\n");
return a - b;
}
static int cal_mul(int a, int b)
{printf("now is mul\r\n");
return a * b;
}
static my_calculate_t s_cal = cal_sum;
static int test2_cal (int a, int b)
{int result = 0;
if(s_cal) {result = s_cal(a ,b);
printf("result=%d\r\n", result);
}
return result;
}
static void my_cal_calculate_register(my_calculate_t cal)
{s_cal = cal;
}
static void my_cal_calculate_unregister(void)
{s_cal = NULL;
}
void app_main(void)
{printf("init done\r\n");
int m = 10, n = 2, ret;
ret = test2_cal(m, n);
my_cal_calculate_register(cal_sub);
ret = test2_cal(m, n);
my_cal_calculate_unregister();
my_cal_calculate_register(cal_mul);
ret = test2_cal(m, n);
my_cal_calculate_unregister();
}
运行结果:
init done
now is sum
result=12
now is sub
result=8
now is mul
result=20
小结:上述程序通过函数my_cal_calculate_register()
动态地改变函数指针s_cal
的值,从而实现在函数中动态地改变test2_cal
功能的目的。一些库文件的源代码是不开放的,因此,一些库中使用这种通过动态注册函数的方法来动态地指定库函数实际功能。
typedef int (*my_calculate_t)(int a, int b);
static int cal_sum(int a, int b)
{printf("now is sum\r\n");
return a + b;
}
static int cal_sub(int a, int b)
{printf("now is sub\r\n");
return a - b;
}
static int cal_mul(int a, int b)
{printf("now is mul\r\n");
return a * b;
}
static int test1_cal (my_calculate_t actual_cal, int a, int b)
{int result = 0;
result = actual_cal(a ,b);
printf("result=%d\r\n", result);
return result;
}
void app_main(void)
{printf("init done\r\n");
int m = 10, n = 2, ret;
ret = test1_cal(cal_sum, m, n);
ret = test1_cal(cal_sub, m, n);
ret = test1_cal(cal_mul, m, n);
}
运行结果:
init done
now is sum
result=12
now is sub
result=8
now is mul
result=20
小结:上面的示例在调用 test1_cal() 时,通过传递要运行的运算函数,实现同一个函数test1_cal
执行多个功能。同名函数传递不同参数执行不同的功能,也是增强函数兼容性的常见方法。
本篇文章重点简述了回调、回调函数的概念。本质上,回调函数是 C 语言中函数指针
的一种用法。回调函数用于在统一的接口内,实现可以动态地改变函数的功能。
回调函数的实现至少需要两个函数,一个是外壳,一个是可以通过函数指针指定实际的被执行的函数。
动态改变回调函数的实现的方法主要有三种:
1)编译时直接赋值
2)运行时实现动态注册
3)作为函数参数传递到指定的函数内
下一节:回调函数的典型使用场景
(码字不易,谢谢点赞或收藏)
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧