目录
1. 函数指针介绍
顾名思义,函数指针是指向函数的指针,而被函数指针指向的函数又被称为回调函数。函数指针的使用步骤共分为两步,函数指针的声明和回调函数的注册。
1.1 函数指针的声明
下面这个用例声明了一个函数指针,函数指针名为ptr,指向的函数返回值类型为BOOL,函数参数一共有两个,一个是int类型的para1,另外一个是char类型的para2。
//函数指针的声明
BOOL *ptr (int para1, char para2);
1.2 对函数进行注册
对函数进行注册的操作实际上就是将函数指针指向回调函数,从而达到间接调用函数的效果,下面的用例就先声明一个函数指针,然后将函数指针指向回调函数,造成的效果和直接调用函数是一致的。
//函数指针的声明
BOOL *ptr (int* para1, int* para2);
/*数值交换函数*/
BOOL swap_value(int *a, int *b)
{
if((NULL == a) || (NULL == b))
{
return FALSE;
}
int temp = *a;
*a = *b;
*b = temp;
return TRUE;
}
int a = 5;
int b = 10;
//注册函数
p = swap_value(&a, &b);
2. 函数指针的优势
(1)灵活度更高:函数指针的灵活性更高,体现在我们可以在需要的时候再对函数进行注册,比如说在私有代码中定义函数指针,这个函数指针可以针对不同的情况指向不同的公共库中的函数,从而更加灵活地实现调用库函数。
(2)可读性更好:函数指针使得代码的可读性更好,例如可以通过将所有的初始化接口被结构体成员中的函数指针注册的方式,将所有的初始化流程都集中在一张表上,从而使得可读性和可为维护性更好。
3. 函数指针应用场景
在C语言中,函数指针常见的应用有两种,其一是函数指针作为结构体成员,其二是函数指针作为函数参数,下面两个用例说明了这两张情况。
3.1 函数指针作为结构体成员
(1)功能背景:
在一个嵌入式系统中,我们期望通过读取系统的逻辑ID来选择不同的方案。逻辑ID为0时,通过Config_para_by_method1函数来配置参数。逻辑ID为1时,通过Config_para_by_method2函数来配置参数,以此类推。
(2)方案设计:
这里最容易想到的是使用if判断语句,判断系统的逻辑ID并配置对应的接口函数。但是这样造成两个问题,一个是当方案数量变多时,代码的圈复杂度过高,难以维护。其次是代码的可读性会变得非常差。
因此我们在这里可以使用函数指针的方案,让函数指针成为结构体成员,让配置函数成为结构体类型的二维数组参数,让其进行注册从而达到把所有的配置函数都写在一张表的效果。在后续操作时,只需要遍历二维数组中的logicID,找到对应的行并且配置对应的回调函数即可。这样大大提高了代码的可维护性和可读性。
#define MAX_LINE sizeof(struct ConfigPara)
//函数指针作为结构体成员
struct ConfigPara
{
unsingned logicId; //逻辑ID
char describe[10]; //描述符
int *pConfigPara(unsigned char logicId); //配置方案
}
//读取当前系统的逻辑ID
unsingned int g_logicId = get_logicID();
//定义结构体变量时注册对应函数
struct ConfigPara g_arr[][MAX_LINE] = {
{ 0, "方案1", Config_para_by_method1}
{ 1, "方案2", Config_para_by_method2}
{ 2, "方案3", Config_para_by_method3}
{ 3, "方案4", Config_para_by_method4}};
//方案配置
for(int index = 0, index < sizeof(g_arr)/sizeof(g_arr[0]), index ++)
{
if(g_logicId == g_arr[index].logicId)
{
g_arr[index]->pConfigPara(g_arr[index].logicId);
}
}
3.2 函数指针作为函数参数
函数指针还可以作为函数参数来传递,这个应用比较简单,比如C标准库中的快速排序函数qsort就是使用了函数指针。
//C标准库快速排序函数定义
void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void*))