上一篇文章讲解了函数指针和指针函数的区别。本文将讲解函数指针数组,并举例应用。
首先回顾一下函数指针:
函数指针是 指向函数的指针 主体是指针 指向的是一个函数的地址(函数也是有地址的!)
基本声明形式:返回数据类型 + (*函数名) + (变量类型1,…);
注意 * 和函数名要用括号括起来,否则因为运算符的优先级原因就变成指针函数了
e.g:
int (*fun) (int);
上面的声明表示:函数返回类型为指针,什么类型的指针呢?就是指向参数为int,返回值为int的函数的指针。(可以类比于指向int型或者char型的指针,int*,char*)
#include
int add(int x,int y)
{
return x + y;
}
int (*fun) (int,int); //声明函数指针
int main()
{
fun = &add; //fun函数指针指向add函数
printf("%d ",fun(3,5));
printf("%d",(*fun)(4,2));
return 0;
}
输出结果:8 6
如果把多个函数指针放在一个数组中,就形成了函数指针数组。
函数指针数组形式如下:
int (*parr[10])(int,int);
parr先和[ ]结合,说明parr是数组,数组的内容是指向int (*)(int,int)类型的函数指针(函数本身的类型一定是参数为(int,int),返回值为(int)),即如上一篇所述,函数指针的参数列表要和函数指针指向的函数的参数列表一致。
这里用加减乘除4个函数为例,演示函数指针数组的应用。
#include
int Test_Add(int a,int b)
{
return a+b;
}
int Test_Sub(int a,int b)
{
return a-b;
}
int Test_Mul(int a,int b)
{
return a*b;
}
int Test_Div(int a,int b)
{
return a/b;
}
void main()
{
int a = 20;
int b = 10;
int (*parr[10])(int,int) = {Test_Add,Test_Sub,Test_Mul,Test_Div};//定义函数指针数组
printf("%d+%d = %d\r\n",a,b,(*parr[0])(a,b));
printf("%d-%d = %d\r\n",a,b,(*parr[1])(a,b));
printf("%d*%d = %d\r\n",a,b,(*parr[2])(a,b));
printf("%d/%d = %d\r\n",a,b,(*parr[3])(a,b));
}
system("pause");
return 0;
这里需要注意函数指针数组的调用,*parr[0]取的是函数指针,即函数的地址;(*parr[0])(a,b)即执行函数,(a,b)是对函数参数赋实参。这里*加不加无所谓,加*可以清楚的指明这是通过指针的方式来调用函数。
第二章已经举例说明了函数指针数组的使用方式。但是,第二章的使用方式必须是函数指针的参数列表要和函数指针指向的函数的参数列表一致。那么能否在函数指针数组中放参数类型不一样的函数指针呢?
比如
int Test_Mod2(int a)
{
return a%2;
}
能够和第二章里面的加减乘除函数都放进一个函数指针数组中呢?
这个问题我在实际应用中碰到过,有实际应用的意义。在做嵌入式时,应用程序(Demo)需要调用BOOT中的函数,首先将BOOT中函数放在一个函数指针数组中;然后将此数组放在一个固定的地址;接着Demo调用BOOT中的函数时,直接去这个固定地址取函数指针,最后传参执行函数。需要调用的API参数类型、返回值是不一样的。
上图是Demo调用BOOT中函数的示意图。
这时候有一个办法:将类型一致的函数放在一个函数指针数组中。但当API较多时,且类型都不一致时,就会导致需要很多函数指针数组。当需要添加函数或者减少函数时,相应的地址都需要变化,很不方便。
还有另一个办法,就是将函数指针数组定义成指向参数和返回值都为void型的函数指针。
如下:
#include
int Test_Add(int a,int b)
{
return a+b;
}
int Test_Sub(int a,int b)
{
return a-b;
}
int Test_Mul(int a,int b)
{
return a*b;
}
int Test_Div(int a,int b)
{
return a/b;
}
int Test_Mod2(int a)
{
return a%2;
}
void(*APITABLE[128])(void)={Test_Add,Test_Sub,Test_Mul,Test_Div,Test_Mod2};
#define Test_Add_Table(a,b) ((*(int(*)(int,int))(*APITABLE[0]))(a,b))
#define Test_Sub_Table(a,b) ((*(int(*)(int,int))(*APITABLE[1]))(a,b))
#define Test_Mul_Table(a,b) ((*(int(*)(int,int))(*APITABLE[2]))(a,b))
#define Test_Div_Table(a,b) ((*(int(*)(int,int))(*APITABLE[3]))(a,b))
#define Test_Mod2_Table(a) ((*(int(*)(int))(*APITABLE[4]))(a))
void pointer_test()
{
int a = 20;
int b = 10;
printf("%d+%d = %d\r\n",a,b,Test_Add_Table(a,b));
printf("%d-%d = %d\r\n",a,b,Test_Sub_Table(a,b));
printf("%d*%d = %d\r\n",a,b,Test_Mul_Table(a,b));
printf("%d/%d = %d\r\n",a,b,Test_Div_Table(a,b));
printf("%d mod2 = %d\r\n",a,Test_Mod2_Table(a));
}
可见,我们将函数指针数组定义成返回值和参数都为空类型即可。使用时,再进行强制转换。
在我的实际应用中,函数指针数组定义是在BOOT中,Define定义是在Demo中,这样就能够顺利调用了。这里我做两个.c文件,一个表示BOOT中函数,一个表示Demo中调用。
//BOOT
int Test_Add(int a,int b)
{
return a+b;
}
int Test_Sub(int a,int b)
{
return a-b;
}
int Test_Mul(int a,int b)
{
return a*b;
}
int Test_Div(int a,int b)
{
return a/b;
}
int Test_Mod2(int a)
{
return a%2;
}
void(*APITABLE[128])(void) _attribute_((at(0x0000A000))) ={Test_Add,Test_Sub,Test_Mul,Test_Div,Test_Mod2};
这里_attribute_((at(0x0000A000)))是ARM编译器规定的指定地址的用法,即将函数指针数组放在0x0000A000地址。
//Demo
#include
#define APITABLE (int*)0x0000A000//与BOOT中定义在同一位置,强制转换成指针
#define Test_Add_Table(a,b) ((*(int(*)(int,int))(*APITABLE[0]))(a,b))
#define Test_Sub_Table(a,b) ((*(int(*)(int,int))(*APITABLE[1]))(a,b))
#define Test_Mul_Table(a,b) ((*(int(*)(int,int))(*APITABLE[2]))(a,b))
#define Test_Div_Table(a,b) ((*(int(*)(int,int))(*APITABLE[3]))(a,b))
#define Test_Mod2_Table(a) ((*(int(*)(int))(*APITABLE[4]))(a))
void pointer_test()
{
int a = 20;
int b = 10;
printf("%d+%d = %d\r\n",a,b,Test_Add_Table(a,b));
printf("%d-%d = %d\r\n",a,b,Test_Sub_Table(a,b));
printf("%d*%d = %d\r\n",a,b,Test_Mul_Table(a,b));
printf("%d/%d = %d\r\n",a,b,Test_Div_Table(a,b));
printf("%d mod2 = %d\r\n",a,Test_Mod2_Table(a));
}
以上就是我在实际应用中用到的函数指针数组的用法。