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
    评论
函数指针是一个指向函数的地址的指针。在C语言中,函数指针常用于回调函数转移。 一、回调函数 回调函数是一种通过函数指针来实现的函数调用方式,它将一个函数指针作为参数传递给另一个函数,使得被调用函数可以在需要的时候调用这个指针所指向的函数。回调函数通常用于事件处理、异步调用和扩展程序等场景。下面是一个简单的示例代码: ```C #include <stdio.h> int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } void calc(int (*func)(int, int), int a, int b) { int result = func(a, b); printf("result: %d\n", result); } int main() { calc(add, 2, 3); // 输出:result: 5 calc(sub, 5, 2); // 输出:result: 3 return 0; } ``` 在上面的代码中,calc函数接受一个函数指针和两个整数参数,它将传入的函数指针所指向的函数作为参数调用,并将返回值输出到控制台。 二、转移 转移是一种将函数指针数组作为参数的编程技巧,它通常用于实现状态机、多路分发等场景。转移可以将复杂的条件判断简化为数组索引,从而提高程序的可读性和可维护性。下面是一个简单的状态机示例代码: ```C #include <stdio.h> typedef enum { STATE_IDLE, STATE_READY, STATE_RUNNING, STATE_STOPPED } State; void idle() { printf("IDLE\n"); } void ready() { printf("READY\n"); } void running() { printf("RUNNING\n"); } void stopped() { printf("STOPPED\n"); } void (*state_table[])(void) = { idle, ready, running, stopped }; int main() { State state = STATE_IDLE; while (1) { state_table[state](); // 状态转移 switch (state) { case STATE_IDLE: state = STATE_READY; break; case STATE_READY: state = STATE_RUNNING; break; case STATE_RUNNING: state = STATE_STOPPED; break; case STATE_STOPPED: state = STATE_IDLE; break; default: break; } } return 0; } ``` 在上面的代码中,state_table是一个函数指针数组,它包含了所有状态对应的函数指针。程序通过循环遍历state_table数组,并根据当前状态调用相应的函数。在状态转移时,使用switch语句实现简单的状态机逻辑。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值