`
blogfeifei
  • 浏览: 1203119 次
文章分类
社区版块
存档分类
最新评论

表驱动法介绍

 
阅读更多

什么是表驱动

  表驱动,又称之为表驱动法、表驱动方法。

  “表”是几乎所有数据结构课本都要讨论的非常有用的数据结构。表驱动方法出于特定的目的来使用表,程序员们经常谈到“表驱动”方法,但是课本中却从未提到过什么是"表驱动"方法。表驱动方法是一种使你可以在表中查找信息,而不必用很多的逻辑语句(if或Case)来把它们找出来的方法。事实上,任何信息都可以通过表来挑选。在简单的情况下,逻辑语句往往更简单而且更直接。但随着逻辑链的复杂,表就变得越来越富有吸引力了,通过下面的这个例子大家就能知道什么是所谓的表驱动方法了。

  假设你需要一个可以返回每个月中天数的函数(为简单起见不考虑闰年),

  一个比较笨的方法是一个大的case语句:

int GetMonthDays(int nMonth)

  {

switch (nMonth)

{
case 1,3,5,7,8,10,12:
day=31; break;
case 4,6,9,11:
day=30; break;
case 2:
day=28; break;
}

return day;

}

  可以看出本来应该很简单的一件事情,代码却是这么冗余,解决这个的办法就可以用表驱动方法。

  static int MonthDays[12] = {31,28,31,30,31,30,31,31,30,31,30,31};

  // 我们可以先定义一个静态数组,这个数组用来保存一年十二个月的天数

  int GetMonthDays(int nMonth)

  {

  return MonthDays[(nMonth - 1)];

  }

  接下来不用多说了,大家都能看出用这种表驱动的方法代替这种情逻辑性不强、但分支很多的代码是多么令人"赏心悦目"的了。

  

函数指针在表驱动方法中的应用:

  在使用表驱动方法时需要说明的一个问题是,你将在表中存储些什么。

  在某些情况下,表查寻的结果是数据。如果是这种情况,你可以把数据存储在表中。

  在其它情况下,表查寻的结果是动作。在这种情况下,你可以把描述这一动作的代码存储在表中。

  在某些语言中,也可以把实现这一动作的子程序的调用存储在表中,也就是将函数的指针保存在表中,当查找到这项时,让程序用这个函数指针来调用相应的程序代码,这个就是函数指针在表驱动方法中的应用。

  其实说到这已经说了很多表驱动方法的相关问题了,现在要把函数指针也应用进去,很多人应该已经想到会是个什么样子了,其实也很简单,通过下面这两段伪代码的例子就可以充分体现函数指针在表驱动方法中应用会使代码更加精致。

  我们在写一段程序的过程中会经常遇到这样的问题,我们在写一个Task的主函数中有时会要等待不同的Event通知,并且处理不同的分支,首先有如下的Event Bit的宏定义和相应的处理函数的声明。

  #define TASK_EVENT_BIT00 (1 << 0)

  #define TASK_EVENT_BIT01 (1 << 1)

  #define TASK_EVENT_BIT02 (1 << 2)

  #define TASK_EVENT_BIT03 (1 << 3)

  #define TASK_EVENT_BIT04 (1 << 4)

  #define TASK_EVENT_BIT05 (1 << 5)

  #define TASK_EVENT_BIT06 (1 << 6)

  #define TASK_EVENT_BIT07 (1 << 7)

  #define TASK_EVENT_BIT08 (1 << 8)

  #define TASK_EVENT_BIT09 (1 << 9)

  void vDoWithEvent00();

  void vDoWithEvent01();

  void vDoWithEvent02();

  void vDoWithEvent03();

  void vDoWithEvent04();

  void vDoWithEvent05();

  void vDoWithEvent06();

  void vDoWithEvent07();

  void vDoWithEvent08();

  void vDoWithEvent09();

  我们一般首先想到的写法是

  unsigned long ulEventBit;

  for(;;)

  {

  xos_waitFlag(&ulEventBit);

  if(ulEventBit & TASK_EVENT_BIT00)

  {

  vDoWithEvent00();

  }

  if(ulEventBit & TASK_EVENT_BIT01)

  {

  vDoWithEvent01();

  }

  if(ulEventBit & TASK_EVENT_BIT02)

  {

  vDoWithEvent02();

  }

  if(ulEventBit & TASK_EVENT_BIT03)

  {

  vDoWithEvent03();

  }

  if(ulEventBit & TASK_EVENT_BIT04)

  {

  vDoWithEvent04();

  }

  if(ulEventBit & TASK_EVENT_BIT05)

  {

  vDoWithEvent05();

  }

  if(ulEventBit & TASK_EVENT_BIT06)

  {

  vDoWithEvent06();

  }

  if(ulEventBit & TASK_EVENT_BIT07)

  {

  vDoWithEvent07();

  }

  if(ulEventBit & TASK_EVENT_BIT08)

  {

  vDoWithEvent08();

  }

  if(ulEventBit & TASK_EVENT_BIT09)

  {

  vDoWithEvent09();

  }

  }

  可以看出这样写是不是显得程序太长了呢。

  下面我们再看看同样的一段代码用函数指针和表驱动方法结合的方法写出会是什么样子。

/* 定义EventBit 与相应处理函数关系的结构体 */

  typedef struct

  {

  unsigned long ulEventBit;

  void (*Func)(void);

  } EventDoWithTable_t;

  

/* 建立EventBit与相应处理函数的关系表 */

  static const EventDoWithTable_t astDoWithTable[] =

{

  { TASK_EVENT_BIT00 , vDoWithEvent00},

  { TASK_EVENT_BIT01 , vDoWithEvent01},

  { TASK_EVENT_BIT02 , vDoWithEvent02},

  { TASK_EVENT_BIT03 , vDoWithEvent03},

  { TASK_EVENT_BIT04 , vDoWithEvent04},

  { TASK_EVENT_BIT05 , vDoWithEvent05},

  { TASK_EVENT_BIT06 , vDoWithEvent06},

  { TASK_EVENT_BIT07 , vDoWithEvent07},

  { TASK_EVENT_BIT08 , vDoWithEvent08},

  { TASK_EVENT_BIT09 , vDoWithEvent09}

  };

  

  unsigned long ulEventBit;

  int i;

  for(;;)

  {

    xos_waitFlag(&ulEventBit);

    for(i = 0 ; i < sizeof(astDoWithTable)/sizeof(astDoWithTable[0]); i ++)

    {

      if ( ( ulEventBit & astDoWithTable[i].ulEventBit ) &&

        ( astDoWithTable[i].Func != NULL ) )

      {

        (*astDoWithTable[i].Func)(); /* 通过函数指针来调用相应的处理函数 */

      }

    }

  }

  可以看出这种代码的风格使代码变得精致得多了,并且使程序的灵活性大大加强了,如果我们还要再加入EventBit,只修改表中的内容就可以了。

  

总结

  通过上面介绍的,相信大家已经对函数指针的使用方法有所了解了,但是需要提醒大家,凡事都要具体情况具体分析,使用函数指针的时候一定要多加小心,因为函数指针有它的一个致命的缺点。

  函数指针的致命缺点是:无法对参数 (parameter) 和返回值 (return value) 的类型进行检查,因为函数已经退化成指针,指针是不带有这些类型信息的。少了类型检查,当参数或者反回值不一致时,会造成严重的错误。有些编译器并不会帮我们找出函数指针这样的致命错误。所以,许多新的编程语言都不支持函数指针了,而改用其他方式。

  从上面的例3中我们可以看到

  int max(int x,int y){ return x>y?x:y; }

  int min(int x,int y){ return x<y?x:y; }

  int add(int x,int y){ return x+y; }

  这三个函数都有两个参数,而在后面却把处理函数定义成

  int process(int x,int y, int (*f)())

  {

    return (*f)(x,y);

  }

  其中第三个参数是一个函数的指针,从表面上看它是个没有参数,并且返回int型的函数的指针,但是在后面却用process(a,b,max)的方式进行调用,max带有两个参数,这段程序在C语言中就可以顺利的编译通过(但是在C++中却编译不通过),可以看出如果编译器没有检查出错误,而我们又不小心写错的话,后果是很严重的,比如return (*f)(x,y);不小心写成return (*f)(x);在C语言中可以正常的被编译通过,但是运行结果一定不是我们想要的。

  因此在C语言中使用函数指针的时候,一定要小心“类型陷阱”,小心地使用函数指针,只有这样我们才可以从函数指针中获益。

分享到:
评论

相关推荐

    深入数据驱动编程之表驱动法的详解

    本篇文章是对表驱动法进行了详细的分析介绍,需要的朋友参考下

    极智魔法师G1950游戏鼠标驱动 官方版

    极智魔法师G1950鼠标驱动是一款为极智魔法师G1950游戏鼠标准备的驱动程序。...极智魔法师G1950鼠标驱动官方介绍汇集世界顶级魔兽公会星辰的操控智慧,提供卓越的游戏体验!全部按键可,欢迎下载体验

    行业-电子政务-水流驱动超大叶轮发电法兼河道疏浚利用法的介绍分析.rar

    行业-电子政务-水流驱动超大叶轮发电法兼河道疏浚利用法的介绍分析.rar

    点阵式液晶显示器的动态驱动法

    以点阵式液晶显示器为例对其动态驱动法作以介绍,给出了一种克服交叉效应的办法。  液晶的显示是由于在显示像素上施加了电场,这个电场是显示像素前后两电极上的电位信号的合成。由于直流电场容易使液晶的寿命降低...

    驱动魔法师简体中文注册版.rar

    软件介绍: 安装说明:软件安装后不要立即打开,复制Language文件夹到C:\Program Files\Driver Magician目录下,覆盖原文件即是中文版。最后再使用压缩包内附的信息注册即可。一款比较专业的驱动管理工具,可以...

    行业-电子政务-水流驱动超大叶轮发电法兼河道疏浚利用法 (1)的介绍分析.rar

    行业-电子政务-水流驱动超大叶轮发电法兼河道疏浚利用法 (1)的介绍分析.rar

    怎么关闭驱动防火墙 高优先率防火墙关闭法【详细介绍】.docx

    怎么关闭驱动防火墙 高优先率防火墙关闭法【详细介绍】.docx

    lcd液晶显示屏的两种驱动方法

    LCD液晶显示屏的驱动方式有许多种,常用的驱动方法有静态驱动法和动态驱动法。下面就由小编为您介绍一下吧。

    Driver Magician驱动备份V4.5中文注册版.rar

    软件介绍: Driver Magician即驱动魔法师,和国内的驱动精灵类似的软件,它能够更新硬件驱动程序,备份本机部分或全部硬盘的驱动,在系统出现故障或系统重装之前,你应该先做好备份以便恢复,可备份 驱动为EXE自...

    用常见的DC/DC芯片做LED恒流驱动电路

    近一个月看了很多的广告式的LED驱动的IC介绍,感到毫无新意,即没有把IC做成真正的LED专用驱动,也...其实每款DC/DC的IC(无论升压或降压)都能接成恒流的LED的驱动,现在分别以KZW3688和CE9908为例介绍一下接法及特点。

    基于项目驱动的Java编程基础教学研究与实践.pdf

    为达到这个目标,本文设计了一种基于项目驱动的教学法,旨在提高课程的教学效果。 在介绍Java语言的重要性和应用广泛性之后,本文概述了当前高职院校Java教学体系的不足之处,包括教师在课程设计、教学内容、实训...

    HAL库驱动详解.pdf

    介绍 STM32CubeTM是意法半导体的一项原始计划,旨在通过减少 开发工作,时间和成本。 STM32CubeTM涵盖了STM32产品组合。 STM32CubeTM版本1.x包括: STM32CubeMX,一种图形软件配置工具,允许生成C初始化 使用...

    《计算机应用基础》中的任务驱动.doc

    《计算机应用基础》中的任务驱动 摘要:本文主要介绍了我校在以就业为导向的课程改革课题中所用到的"任务驱动"教 学法的具体实施步骤,并将其和传统教学法作了几点比较,提出了实施"任务驱动"教学 法时应注意的几点...

    大白话领域驱动设计DDD视频教程

    课程介绍 抛开杂念,看看传统三层CRUD编程方式 DDD领域驱动设计到底是什么? DDD和传统三层优劣势比较 DDD在国内现象是个什么情况? DDD从战略设计到战术设计概览 第2章 领域分析模型 核心域,支撑子域,通用子域 ...

    多机驱动带式输送机功率平衡的研究与分析

    文章介绍了液体黏性传动装置的结构与工作原理及其液压伺服控制系统原理,给出了一种利用液体黏性传动装置实现带式输送机功率平衡调节的电流控制法原理,即通过改变液体黏性传动装置所配用的液压伺服控制系统中各个电液...

    基于“任务驱动”的CAD教学方法探讨

    介绍了利用"任务驱动"教学法设计的CAD教学方法,从设计任务、分析任务、上机辅导(实践任务)和评价任务几个方面阐述了"任务驱动"教学法与CAD教学相结合的具体教学模式,改变了以往"教师讲,学生听"、以教定学的被动教学...

    基于任务驱动法的_Java程序设计_有效课堂教学(毕设&课设论文参考).pdf

    【1】该资源属于项目论文,非项目...【3】文章适合学习借鉴,为您的项目开发或写作提供专业知识介绍及思路,不推荐完全照抄。 【4】毕业设计、课程设计可参考借鉴! 重点:鼓励大家下载后仔细研读学习,多看、多思考!

    带式输送机驱动滚筒的有限元分析

    主要研究带式输送机驱动滚筒不同轮辐间距对滚筒受力变化的影响,简单介绍有限元分析法和三维建模,然后对驱动滚筒进行受力分析,最后把ANSYS有限元法和CAE技术结合起来对驱动滚筒进行设计分析。

    ARM9 的WinCE 驱动程序设计

    由ARM9微处理器构成的便携式大容量数据采集系统,是典型的测试应用系统,可以在 现场实时采集和存储多达8路、12M字节的数据,数据可长期保存。...法,包括流式接口驱动的编写、系统启动加载和中断初始化等实用技术。

Global site tag (gtag.js) - Google Analytics