C:函数指针数组及驱动表程序解读

来源:岁月联盟 编辑:猪蛋儿 时间:2012-05-19

上一节http://www.2cto.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的专栏

图片内容