织梦CMS - 轻松建站从此开始!

罗索

C语言中使用函数指针构造回调函数的一个典型应用

jackyhwei 发布于 2010-03-14 18:57 点击:次 
这是一个简单的例子,虽然说明了回调函数的构造方法和使用,但是只是个简单的应用,还没有完全体现出来回调函数的强大功能。这个函数的扩展性很好,大家可以看见传递的参数都是(void*),也就是一个通用性指针,可以指向任意类型。
TAG:

//////////////////////////////////////
/*
这个例子比较复杂,需要放到专门的编译器中去 看,建议使用VS查看
在后面的帖子中有一个简单的例子说明回调函数的用法,比较清楚,可以参看
*/

//主函数调用:
int _tmain(int argc, _TCHAR* argv[])
{
Init();
........
AddFileDriver(MiddleWare,NULL);
......
_getch();
return 0;
}

//
//AddFileDriver的代码
//加载底层驱动程序。典型的回调函数使用<驱动程序DiskCommand通过 AddFileDriver获取参数Parameter信息>
//个人认为可以改成更通用的形 式:AddFileDriver(int8  (* DiskCommand)(void *Parameter), void *RsvdForLow)
//其中参数:Parameter可以获取调用时的动态信息,比如:命令字、要操作的扇区
//   RsvdForLow可以获取程序运行过程中的静态信息,比如:固定的磁盘信息
//   注意:Parameter,RsvdForLow传递到DiskCommand函数中具体使用时都要强制类型转化

//Parameter 用于向驱动函数<DiskCommand指向的函数>传递信息,Parameter->RsvdForLow 获取外部的其他辅助信息
void AddFileDriver(int8  (* DiskCommand)(int8 Command, void *Parameter), void *RsvdForLow)
{
//1. 检测是否有初始化程序<防止传递空参数>,初始化是否成功<物理磁盘是否可操作>
//2.获取当前第一个空闲逻辑盘并挂 接<也就是将物理磁盘转化为系统内部表示法以便操作>
//3.检测是否挂接成功。失败:退出。成功:读取物理磁盘0扇区(启动扇区) 的值
//4.根据启动扇区数据重新设置逻辑盘参数(第一次有意义的初始化)
  //**此时需要判断磁盘是否正确格式化了,如果没有格式 化(Format),那么此时需要运行Format程序

int8  Buf[SECSIZE] = {0}; //存储一个扇区的内容
    Disk_Info *Disk = NULL;
Disk_RW_Parameter Pa;

Pa.TempVal = 0;     //初始化,无意义
Pa.SectorIndex = 0;    //初始化,无意义
    Pa.RsvdForLow = RsvdForLow;  //相关辅助信息,有可能用到,如果调用函数RsvdForLow = NULL,那么这个参数也就无意义
    Pa.Buf = Buf;     //便于调用驱动函数运行时产生的数据,一般是磁盘读写数据的缓存
Pa.Drive = EMPTY_DRIVE;   //初始化,当前使用的逻辑盘符

if (DiskCommand == NULL)        // 参数无效退出
    {
        return;
    }

//先挂接再初始化,把流程中所 说的1和2有所颠倒,为了统一接口
Disk = GetEmptyDiskInfoAddr();
if (Disk == NULL)
    {
        return;
    }

//Disk是指向全局变量DiskInfo_G的指针,对其 赋值可以把信息保留到程序运行完毕
//因此把程序运行过程中一直需要使用的信息传给Disk->RsvdForLow和 Disk->DiskCommand
//Disk->DiskCommand 一般指向驱动函数,Disk->RsvdForLow 指向主函数中传递过来的信息
Disk->RsvdForLow = RsvdForLow;  //接收辅助信息
Disk->DiskCommand = DiskCommand; //指向驱动程序

Pa.Drive = Disk->Drive;
    if (Disk->DiskCommand(DISK_INIT, &Pa) != DISK_INIT_OK)   // 底层驱动初始化不成功退出
    {
        return;
    }

//读取0扇区的数 据,初始化DiskInfo_G[Drive]
Pa.SectorIndex = 0;
    if(Disk->DiskCommand(DISK_READ_SECTOR, &Pa) != DISK_READ_OK)
    {
        return;
    }

#ifdef NF_FORMAT  //如果没有没有格式化便格式化
if(FSMount(Disk, Buf) != RETURN_OK)
{
  Format(Disk->Drive);
  if(FSMount(Disk, Buf) != RETURN_OK)
   return;
}
#else
FSMount(Disk, Buf);
#endif

}
//****************Disk_RW_Parameter的结构如 下***********************//
typedef struct _Disk_RW_Parameter
{
int32  TempVal;  /* 用于传递函数需要的参数或者将函数调用后产生的值传出来 */
        /* 常用于传递一个需要操作的函数或者传回函数调用后产生的值*/
    uint32      SectorIndex;    /* 操作的扇区,当向下传递一个参数的时候也可以使用TempVal替代 */
    void        *RsvdForLow;    /* 保留给底层驱动程序,由_Disk_Info中拷贝过来 */
    int8  *Buf;           /* 数据存储位置 */
    int8  Drive;
}Disk_RW_Parameter;
//************************************************************************************************************************************//


//
//MiddleWare 的代码
int8 MiddleWare(int8 Command, void *Parameter)
{
    uint8 state;    //用于返回值,表示函数操作完毕之后的状态
    Disk_RW_Parameter * Dp;  //用于接收加载驱动时传递的参数
   
    Dp = (Disk_RW_Parameter *)Parameter;
   
    // 如果所要求的命令没有在这里实现,则state =  BAD_DISK_COMMAND
    switch (Command)
    {
  default:
            state = BAD_DISK_COMMAND;
            break;        
  case DISK_INIT:
            // 初始化驱动程序,必须实现 //
            // Parameter没有使用 //
   //state= DISK_INIT_OK 或 DISK_INIT_NOT_OK
            //state = PHDisk_Initialize();为了明确显示采用以下方式:
   if(PHDisk_Initialize() == PH_DISK_INIT_OK)
    state = DISK_INIT_OK;
   else
    state = DISK_INIT_NOT_OK;
            break;
  case DISK_READ_SECTOR:
            // 读物理扇区,必须实现 //
            // Dp->Buf:存储读到的数据 //
            // Dp->SectorIndex:物理扇区索引 //
            // state=DISK_READ_OK或DISK_READ_NOT_OK//
   if(PHDiskReadSec(Dp->SectorIndex, Dp->Buf) == PH_DISK_READ_OK)
    state = DISK_READ_OK;
   else
    state = DISK_READ_NOT_OK;
            break;
.......
.......
    }
    return state;
}

注意红色和大字体部分,可以放到VS中去看,网页上显示的比较乱!
之所以贴出代码是因为这段代码实在是太经典了,用好
void AddFileDriver(int8  (* DiskCommand)(int8 Command, void *Parameter), void *RsvdForLow)
中的void *Parameter和void *RsvdForLow,可以实现强大的变参函数。而且函数接口具有良好的扩展性。 

举一个简单的回调函数的例子吧(出自 《C和指针》13.3.1节,网上有电子版下载,图书馆好像也有此书)

//使用函数指针构造回调函数
//在一个单链 表中查找一个指定的值(*value)
search_list(Node *node, void const *value, int (*compare)(void const *, void const *) )
{

         while(node != NULL)
         {
                 if(compare(&node->value, value) == 0 )
                          break;
                 node = node->link;
         }
         return node;
}


//函数的定义
int compare_ints (void const *a, void const *b)
{
         if(*(int*)a == *(int*)b )
                return 0;
         else
                return 1;
}

//回调函数的使用
desired_node = search_list(root, &desired_value, compare_ints);


***这是一个 简单的例子,虽然说明了回调函数的构造方法和使用,但是只是个简单的应用,还没有完全体现出来回调函数的强大功能。这个函数的扩展性很好,大家可以看见传 递的参数都是(void*),也就是一个通用性指针,可以指向任意类型。但是没有体现出来被调用函数可以从回调函数接收参数这一点,但是在第一个例子中具 有这种思想(在回调函数AddFileDriver(...)中给(Disk_RW_Parameter Pa) 赋值,然后再传给被调用函数MiddleWare(...),也就是*DiskCommand指向的函数)
(hongjiujing)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201003/8746.html]
本文出处:CSDN博客 作者:hongjiujing
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容