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

来源:互联网 发布:php网页游戏源码 编辑:程序博客网 时间:2024/05/18 17:43

//////////////////////////////////////
/*
这个例子比较复杂,需要放到专门的编译器中去看,建议使用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指向的函数)
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 微信小程序斗地主被限制了怎么办 台式电脑玩斗地主总黑屏怎么办 玩斗地主屏幕出现一半玩不了怎么办 电脑qq文件破损或部分丢失怎么办 华为平板电脑开机密码忘记了怎么办 微信被太多人投诉被限制登录怎么办 微信账号被永久封号里面的钱怎么办 乱世王者领礼包时账号异常怎么办 qq填写资料见证号格式错误怎么办 如果把微信注销 王者的号怎么办 93元的吃鸡号忘了激活吗怎么办 王者荣耀实名注册不是自己的怎么办 苹果手机打开qq太慢了怎么办 剪辑视频打开了软件关闭不了怎么办 玩永恒纪元手游网络老掉线怎么办 绝地求生买的钥匙激活码忘了怎么办 魅族手机移动网络打不开网页怎么办 小米5s升级后下载不了软件怎么办 电脑可以登qq却开不了网页怎么办 手机微信图片没下载原图怎么办 qq号密码忘了密保忘了怎么办 扣扣更改密保手机失败怎么办 至尊宝安全模式密保手机更换怎么办 微信号手机号换了密码忘记了怎么办 被加盟网店托管骗了怎么办 善林金融倒闭投资者的钱怎么办? 微信支付密码忘了怎么办没绑卡 美团外卖没有骑手接单怎么办 发微信的"发送"没有了怎么办 华硕电脑下面的任务栏卡住了怎么办 微信登别人电脑上忘记退了怎么办 买手机买全新结果买到翻新机怎么办 苹果手机激活锁忘了id账号怎么办 淘宝很多产品都需要3c怎么办 小米mix装在兜里还能解锁怎么办 痰咳不出来憋的嘴唇紫了怎么办 京东闪电退款后不给退货怎么办 我的中国银行卡在异地被冻结怎么办 qq密保号码换了验证不了怎么办 qq登陆需要密保手机验证怎么办 微信漂流瓶被投诉收不到回复怎么办