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

简介        

          函数指针是指向函数的指针变量。 因而“函数指针”本身首先应是指针变量,只不过该指针变量指向函数。这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。如前所述,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 0
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
};
#define N_CATEGORIES\
		(sizeof(chartest)/sizeof(chartest[0]))
/*使用这种宏,能使代码段更具可拓展性*/
#if ENUMVERSION
typedef 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更为安全。

  

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值