C函数指针应用之转移表(jump tables)

来源:互联网 发布:阿里云 专线 编辑:程序博客网 时间:2024/06/09 13:47

简介        

          函数指针是指向函数的指针变量。 因而“函数指针”本身首先应是指针变量,只不过该指针变量指向函数。这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。如前所述,C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上是大体一致的。

          函数指针的应用最多的是转移表(jump table)回调函数(callback function)

          使用转移表可以替代冗长的switch和if-else语句,分离了具体操作和选择代码,是一种良好的设计方案。和其他指针一样,对函数指针执行间接访问之前,必须把它初始化并指向某一个函数。

详解

       下面举一个例子说明什么是转移表:

         Q:编写一个程序,从标准输入读取一串字符,根据ctype.h中函数定义的字符分类,计算各类字符所占的百分比。不能使用一系列If语句。

            Solution:

          事实上转移表就是一个函数指针数组,声明并初始化一个数组

          确保函数原型出现在这个数组声明之前,在使用转移表这个指针数组时特别要注意下标引用不要越界,否则会引起不可预知的后果。

          函数在被使用时总是由编译器把它转换为函数指针。

          接着我们就能用这个转移表来替换switch和if了。

int isnotprint(int c){if(isprint(c))return FALSE; return TRUE;}/*jump table of chartest,each function return none-zero or FALSE*/int (*chartest[])( int )={ iscntrl, isspace, isdigit, islower, isupper, ispunct, isnotprint};
完整的代码:
#include<stdio.h>#include<stdlib.h>#include<ctype.h>#define TRUE 1#define FALSE 0#define MAX_LINE_LENTH 1024 /*define the max buffer*/#define ENUMVERSION 0int isnotprint(int c){if(isprint(c))return FALSE;return TRUE;}/*jump table of chartest,each function return none-zero or FALSE*/int (*chartest[])( int )={iscntrl,isspace,isdigit,islower,isupper,ispunct,isnotprint};#define N_CATEGORIES\(sizeof(chartest)/sizeof(chartest[0]))/*使用这种宏,能使代码段更具可拓展性*/#if ENUMVERSIONtypedef enum{cntrl,space,digit,lower,upper,punct,print}charType;#endif /*ENUM VERSION ONLY*//*printing label*/char* label[]={" control"," space"," digit"," lower letter"," upper letter"," punct letter"," unprinted letter"};int main(){char buffer[MAX_LINE_LENTH],*str;//声明缓冲区float num[7]={0,0,0,0,0,0,0};//用于记录各种字符数量的数组float sum=0;//字符总数                int i;                            printf("Please input the string:\n");        str=fgets(buffer,MAX_LINE_LENTH,stdin);//从标准输入读取一串字符if(str==NULL){exit(1);//如果失败}                /*判断字符种类,使用转移表和循环代替switch和if*/                while( (*str++)!='\0' ){++sum;for(i=0;i<N_CATEGORIES;i++){if(FALSE!=chartest[i](*str))     ++num[i];}}        printf("the total character number:%3f\n",sum);                 for(i=0;i<N_CATEGORIES;i++){printf("the%s count:%3f  %3f%%\n",label[i],num[i],100*num[i]/sum);}return 0;     }

技巧

           在这里使用了一个宏:

#define N_CATEGORIES\(sizeof(chartest)/sizeof(chartest[0]))

           使用这样一个宏计算转移表的长度,在使用转移表的时候,这个宏可以作为循环条件,一定程度上,改善了代码的易扩展性

           在从标准输入输入一串字符串的时候,用的是fgets而不是gets,原因是gets函数并没有指定缓冲区长度,这个函数只能用于玩具程序,使用fgets更为安全。

  

0 0
原创粉丝点击