上一篇文章講解了函數指針和指針函數的區別。本文將講解函數指針數組,並舉例應用。
一.函數指針
首先回顧一下函數指針:
函數指針是 指向函數的指針 主體是指針 指向的是一個函數的地址(函數也是有地址的!)
基本聲明形式:返回數據類型 + (*函數名) + (變量類型1,…);
注意 * 和函數名要用括號括起來,否則因爲運算符的優先級原因就變成指針函數了
e.g:
int (*fun) (int);
上面的聲明表示:函數返回類型爲指針,什麼類型的指針呢?就是指向參數爲int,返回值爲int的函數的指針。(可以類比於指向int型或者char型的指針,int*,char*)
#include<stdio.h>
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<stdio.h>
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<stdio.h>
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 <stdio.h>
#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));
}
以上就是我在實際應用中用到的函數指針數組的用法。