上一節http://www.BkJia.com/kf/201205/132375.html解讀了C程序中函數指針及回調函數的寫法,本節再看一下函數指針另一個較為廣泛的應用-驅動表程序,在這之前,首先需要了解函數指針數組的使用,依舊通過最簡單最容易理解的例子來講解。
首先看下面這個函數指針數組的使用實例。
[cpp] #include <stdio.h>
#include <stdlib.h>
int Sum(int a, int b)
{
return a + b;
}
int Sub(int a, int b)
{
return a - b;
}
typedef int (*pfFun)(int, int);
int TestFun(int a, int b, pfFun pf)
{
int i = 0;
i = pf(a, b);
return i;
}
int main(int argc, char *argv[])
{
int iTmp = 0;
pfFun pf[] = {Sum, Sub}; /*定義並一個函數指針數組,包含2個元素,並將其初始化為Sum和Sub函數地址*/
iTmp = TestFun(20, 10, pf[0]);
printf("Tmp is: %d\n", iTmp);
iTmp = TestFun(20, 10, pf[1]);
printf("Tmp is: %d\n", iTmp);
system("PAUSE");
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int Sum(int a, int b)
{
return a + b;
}
int Sub(int a, int b)
{
return a - b;
}
typedef int (*pfFun)(int, int);
int TestFun(int a, int b, pfFun pf)
{
int i = 0;
i = pf(a, b);
return i;
}
int main(int argc, char *argv[])
{
int iTmp = 0;
pfFun pf[] = {Sum, Sub}; /*定義並一個函數指針數組,包含2個元素,並將其初始化為Sum和Sub函數地址*/
iTmp = TestFun(20, 10, pf[0]);
printf("Tmp is: %d\n", iTmp);
iTmp = TestFun(20, 10, pf[1]);
printf("Tmp is: %d\n", iTmp);
system("PAUSE");
return 0;
}
運行一下:
[plain]
Tmp is: 30
Tmp is: 10
請按任意鍵繼續. . .
Tmp is: 30
Tmp is: 10
請按任意鍵繼續. . .
有了上面的概念,讓我們通過另一個實例看看驅動表的使用,下面這個小程序幾乎每個程序員都應該寫過,一個沒有考慮精度的加減乘除運算程序,如下:
[cpp]
#include <stdio.h>
#include <stdlib.h>
/*加法*/
int Sum(int a, int b)
{
return a + b;
}
/*減法*/
int Sub(int a, int b)
{
return a - b;
}
/*乘法*/
int Multi(int a, int b)
{
return a * b;
}
/*除法*/
int Division(int a, int b)
{
return (b == 0)? 0:(a / b);
}
/*操作碼*/
typedef enum _ENOPCODE
{
OPCODE_ADD = 0, /*加*/
OPCODE_SUB, /*減*/
OPCODE_MULTI, /*乘*/
OPCODE_DIVISION, /*除*/
OPCODE_BUTT
}enOpCode;
/*通過Switch-case語句計算*/
int GetOpResultBySwitch(int a, int b, enOpCode enOp)
{
int iTmp = 0;
switch(enOp)
{
case OPCODE_ADD:
iTmp = Sum(a, b);
break;
case OPCODE_SUB:
iTmp = Sub(a, b);
break;
case OPCODE_MULTI:
iTmp = Multi(a, b);
break;
case OPCODE_DIVISION:
iTmp = Division(a, b);
break;
default:
iTmp = -1;
}
return iTmp;
}
int main(int argc, char *argv[])
{
int iTmp = 0;
int a = 10;
int b = 30;
iTmp = GetOpResultBySwitch(a, b, OPCODE_ADD);
printf("Tmp is: %d\n", iTmp);
system("PAUSE");
return 0;
}
#include <stdio.h>
#include <stdlib.h>
/*加法*/
int Sum(int a, int b)
{
return a + b;
}
/*減法*/
int Sub(int a, int b)
{
return a - b;
}
/*乘法*/
int Multi(int a, int b)
{
return a * b;
}
/*除法*/
int Division(int a, int b)
{
return (b == 0)? 0:(a / b);
}
/*操作碼*/
typedef enum _ENOPCODE
{
OPCODE_ADD = 0, /*加*/
OPCODE_SUB, /*減*/
OPCODE_MULTI, /*乘*/
OPCODE_DIVISION, /*除*/
OPCODE_BUTT
}enOpCode;
/*通過Switch-case語句計算*/
int GetOpResultBySwitch(int a, int b, enOpCode enOp)
{
int iTmp = 0;
switch(enOp)
{
case OPCODE_ADD:
iTmp = Sum(a, b);
break;
case OPCODE_SUB:
iTmp = Sub(a, b);
break;
case OPCODE_MULTI:
iTmp = Multi(a, b);
break;
case OPCODE_DIVISION:
iTmp = Division(a, b);
break;
default:
iTmp = -1;
}
return iTmp;
}
int main(int argc, char *argv[])
{
int iTmp = 0;
int a = 10;
int b = 30;
iTmp = GetOpResultBySwitch(a, b, OPCODE_ADD);
printf("Tmp is: %d\n", iTmp);
system("PAUSE");
return 0;
}
程序看上去很清晰,但如果要擴展一下功能,就發現要增加更多的case語句,記得ansi c標准中case的最大個數是256個,暫且不論這個值到底是多少,從代碼本身來看,增加過多的case使得圈復雜度不斷上升,程序維護困難加大。
這時就可以考慮使用驅動表的方法,同樣看一下實現,請關注GetOpResultByTable函數。
[cpp]
#include <stdio.h>
#include <stdlib.h>
/*加法*/
int Sum(int a, int b)
{
return a + b;
}
/*減法*/
int Sub(int a, int b)
{
return a - b;
}
/*乘法*/
int Multi(int a, int b)
{
return a * b;
}
/*除法*/
int Division(int a, int b)
{
return (b == 0)? 0:(a / b);
}
/*定義函數指針*/
typedef int (*pfFun)(int, int);
/*操作碼*/
typedef enum _ENOPCODE
{
OPCODE_ADD = 0, /*加*/
OPCODE_SUB, /*減*/
OPCODE_MULTI, /*乘*/
OPCODE_DIVISION, /*除*/
OPCODE_BUTT
}enOpCode;
/*使用驅動表計算*/
int GetOpResultByTable(int a, int b, enOpCode enOp)
{
if (OPCODE_BUTT == enOp)
{
return -1;
}
pfFun pf[OPCODE_BUTT] = {Sum, Sub, Multi, Division};
return pf[enOp](a, b);
}
int main(int argc, char *argv[])
{
int iTmp = 0;
int a = 10;
int b = 30;
iTmp = GetOpResultByTable(a, b, OPCODE_ADD);
printf("Tmp is: %d\n", iTmp);
system("PAUSE");
return 0;
}
#include <stdio.h>
#include <stdlib.h>
/*加法*/
int Sum(int a, int b)
{
return a + b;
}
/*減法*/
int Sub(int a, int b)
{
return a - b;
}
/*乘法*/
int Multi(int a, int b)
{
return a * b;
}
/*除法*/
int Division(int a, int b)
{
return (b == 0)? 0:(a / b);
}
/*定義函數指針*/
typedef int (*pfFun)(int, int);
/*操作碼*/
typedef enum _ENOPCODE
{
OPCODE_ADD = 0, /*加*/
OPCODE_SUB, /*減*/
OPCODE_MULTI, /*乘*/
OPCODE_DIVISION, /*除*/
OPCODE_BUTT
}enOpCode;
/*使用驅動表計算*/
int GetOpResultByTable(int a, int b, enOpCode enOp)
{
if (OPCODE_BUTT == enOp)
{
return -1;
}
pfFun pf[OPCODE_BUTT] = {Sum, Sub, Multi, Division};
return pf[enOp](a, b);
}
int main(int argc, char *argv[])
{
int iTmp = 0;
int a = 10;
int b = 30;
iTmp = GetOpResultByTable(a, b, OPCODE_ADD);
printf("Tmp is: %d\n", iTmp);
system("PAUSE");
return 0;
}
實現相當簡單,如果增加其他操作等功能,僅需要擴展pf數組,程序圈復雜度不會隨功能增多而增加,從而也降低了維護成本。
附:圈復雜度概念,來自百度百科:http://baike.baidu.com/view/3553594.htm
圈復雜度
概念
所謂圈復雜度是一種代碼復雜度的衡量標准,中文名稱叫做圈復雜度。在軟件測試的概念裡,圈復雜度“用來衡量一個模塊判定結構的復雜程度,數量上表現為獨立現行路徑條數,即合理的預防錯誤所需測試的最少路徑條數,圈復雜度大說明程序代碼可能質量低且難於測試和維護,根據經驗,程序的可能錯誤和高的圈復雜度有著很大關系”。
計算
它的計算方法很簡單,計算公式為:V(G)=e-n+2。其中,e表示控制流圖中邊的數量,n表示控制流圖中節點的數量。其實,圈復雜度的計算還有更直觀的方法,因為圈復雜度所反映的是“判定條件”的數量,所以圈復雜度實際上就是等於判定節點的數量再加上1,也即控制流圖的區域數,對應的計算公式為:V(G)=區域數=判定節點數+1。 h r0U&T#@-g o,J o114943 對於多分支的CASE結構或IF-ELSEIF-ELSE結構,統計判定節點的個數時需要特別注意一點,要求必須統計全部實際的判定節點數,也即每個ELSEIF語句,以及每個CASE語句,都應該算為一個判定節點。判定節點在模塊的控制流圖中很容易被識別出來,所以,針對程序的控制流圖計算圈復雜度V(G)時,最好還是采用第一個公式,也即V(G)=e-n+2;而針對模塊的控制流圖時,可以直接統計判定節點數,這樣更為簡單。
摘自 Socrates的專欄