指針一直都是c語言中的難點,但是指針在使用中,更多的情況是指向數組、變量等類型。而函數指針一般比較少直接運用。對於函數指針,最常見的兩個用途就是轉移表和作爲參數傳遞給另一個函數。函數指針也和其他指針一樣,對函數指針進行訪問前必須把它初始化爲指向某個函數。
首先講一下回調函數,回調函數是指用戶把一個函數指針作爲參數傳遞給其他函數,後者將“回調”用戶所傳遞的函數,使用這樣技巧的函數稱回調函數。舉個例子來說,在某個時刻,需要比較兩個整形值的大小,從而進行相應的操作,這樣的情況下,當然可以通過編寫一個參數爲整形的比較函數來實現,在需要調用的地方進行調用,但是如果在代碼中另一處又想比較浮點型的參數的大小呢,是不是又要編寫一個浮點型參數的比較函數,假如還要比較字符串呢(當然了字符串的話,有庫函數可以調用,這裏只是舉例說明情況而已),可以看到不同類型的比較,都需要相應的接口函數實現,那麼能不能實現一個函數,只管傳遞參數,不用管參數的類型,也能得到正確的比較結果,答案是可以的。這樣一來就可以統一對外提供的接口,做到便於代碼的管理,所使用的技巧就是回調函數。下面就以實現比較函數的回調函數來作爲例子,來說明如何實現回調函數的用法。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int compare_type_int(void const *va, void const *vb)
{
int a = *(int *)va;
int b = *(int *)vb;
if (a == b)
{
return 0;
}
else
{
return -1;
}
}
int compare_type_str(void const *va, void const *vb)
{
char *a = (char *)va;
char *b = (char *)vb;
return strcmp(a,b);
}
int compare_function(void const *ta, void const *tb, int (*compare)(void const *a, void const *b))
{
return compare(ta, tb);
}
int main(void)
{
int val_a = 5;
int val_b = 5;
char *str_a = "abc";
char *str_b = "abd";
if (compare_function((void const *)&val_a, (void const *)&val_b, compare_type_int) == 0)
printf("val_a == val_b\n");
else
printf("val_a != val_b\n");
if (compare_function((void const *)&str_a, (void const *)&str_b, compare_type_str) == 0)
printf("str_a == str_b\n");
else
printf("str_a != str_b\n");
system("pause");
return 0;
}
函數指針的第二用法就是實現轉移表。以整形數的加減乘除操作爲例來說明轉移表的實現方式(這裏旨在說明用法,不作函數健壯性判斷)。
#include <stdio.h>
#include <stdlib.h>
int ope_add(int a, int b)
{
return a + b;
}
int ope_sub(int a, int b)
{
return a - b;
}
int ope_mul(int a, int b)
{
return a * b;
}
int ope_div(int a, int b)
{
return a / b;
}
int main(void)
{
int a;
int b;
int oper_type;
int result;
scanf("%d%d%d",&a, &b, &oper_type);
switch(oper_type)
{
case 1:
result = ope_add(a,b);
break;
case 2:
result = ope_sub(a, b);
break;
case 3:
result = ope_mul(a, b);
break;
case 4:
result = ope_div(a, b);
break;
}
printf("result = %d\n",result);
system("pause");
return 0;
}
可以看到,對於指定的操作類型,在實現中調用相應的函數。但是如果這樣的操作類型遠不止例子中的4個,則switch中的case語句將會是相當多的,整個函數的代碼會也因此而變得很長,不利於程序閱讀和維護。這時就可以用轉移表來作替代實現。代碼如下:
#include <stdio.h>
#include <stdlib.h>
int ope_add(int a, int b)
{
return a + b;
}
int ope_sub(int a, int b)
{
return a - b;
}
int ope_mul(int a, int b)
{
return a * b;
}
int ope_div(int a, int b)
{
return a / b;
}
int (*oper_fun[])(int a, int b) = {ope_add, ope_sub, ope_mul, ope_div};
int main(void)
{
int a;
int b;
int oper_type;
int result;
scanf("%d%d%d",&a, &b, &oper_type);
if (oper_type>= 0 && oper_type <= 3)
result = oper_fun[oper_type](a, b);
else
{
printf("no such operation\n");
return -1;
}
printf("result = %d\n",result);
system("pause");
return 0;
}
定義一個函數指針數組,這樣當需要添加新的操作類型的函數時,只需要定義該類型的函數,並添加數組成員,就可以了。對於操作類型oper_type要做一個安全性判斷,如果發生下標越界訪問,在一個複雜的程序代碼中,將會是很難調節的,因爲其結果是未定義的,並不能知道程序什麼時候會運行失敗,所以在寫代碼時,做好這一步健壯性判斷是必須的。但是假如某程序中使用了轉移表,而在實現調試過程中,懷疑轉移表訪問出問題了,可以在那個函數調用前後各打印一條信息。因爲如果轉移表訪問越界了,函數調用是不會返回的。用這種辦法就可以判斷是不是轉移表訪問越界了。
轉移表雖然方便,但也是也有限制,就是轉移表內的函數類型要相同。