工程化编程实战callback接口学习笔记

一、实验要求

  • 在VSCode下编译运行lab5-1.tar.gz 

  • 通过VSCode+GDB调试程序找出quit命令无法运行的bug产生的原因

  • 分析callback接口的运行机制,总结callback接口设计的方法

二、实验过程

      首先安装好vscode和MinGW并配置好环境变量。

      在vscode下导入实验所需要的代码,点击debug按钮,添加配置,选择C/C++ gdb启动,配置好launch.json和tasks.json,

编译运行,按要求输入quit命令,发现输出为This is a wrong cmd!

      我们来检查一下menu.c的源码:

    while(1)
    {
        printf("Input a cmd number > ");
        scanf("%s", cmd);
        tDataNode *p = FindCmd(head, cmd);
###########
if( p == NULL) { printf("This is a wrong cmd!\n "); continue; } printf("%s - %s\n", p->cmd, p->desc); if(p->handler != NULL) { p->handler(); } }

      很明显可以看到由于p为空指针,所以输出了错误信息,由于p指针是由FindCmd函数返回的,我们接着查看FindCmd函数。

tDataNode* FindCmd(tLinkTable * head, char * cmd)
{
    return  (tDataNode*)SearchLinkTableNode(head,SearchCondition);
}

       很明显,我们需要继续查看SearchLinkTableNode函数,在linktable.c文件中找到SearchLinkTableNode函数,我们来继续检查

#int Conditon(tLinkTableNode * pNode)调用了回调函数
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode))
{
    if(pLinkTable == NULL || Conditon == NULL)
    {
        return NULL;
    }
    tLinkTableNode * pNode = pLinkTable->pHead;
#此处循环退出条件为指针遍历至最后一个节点时退出循环
while(pNode != pLinkTable->pTail) { if(Conditon(pNode) == SUCCESS) { return pNode; } pNode = pNode->pNext; } return NULL; }

     分析了以上代码可以发现,输入quit命名返回错误信息是因为SearchLinkTableNode函数返回了NULL指针,很明显返回NULL指针只有在pLinkTable和Conditon至少有一个为NULL或者直到pNode循环遍历结束以后也未满足Conditon(pNode) == SUCCESS的条件,很显然前者我们可以排除,我们来分析后者的情况。

     首先我们要知道pLinkTable->pTail是什么

#LinkTable的数据结构
typedef struct LinkTable { tLinkTableNode *pHead; tLinkTableNode *pTail; int SumOfNode; pthread_mutex_t mutex; }tLinkTable;
int AddLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode)
{
    if(pLinkTable == NULL || pNode == NULL)
    {
        return FAILURE;
    }
    pNode->pNext = NULL;
    pthread_mutex_lock(&(pLinkTable->mutex));
    if(pLinkTable->pHead == NULL)
    {
        pLinkTable->pHead = pNode;
    }
    if(pLinkTable->pTail == NULL)
    {
        pLinkTable->pTail = pNode;
    }
#由此可知pTail指向的是最后一个节点
else { pLinkTable->pTail->pNext = pNode; pLinkTable->pTail = pNode; } pLinkTable->SumOfNode += 1 ; pthread_mutex_unlock(&(pLinkTable->mutex)); return SUCCESS; }

       通过分析以上的代码,我们很容易发现,当pNode遍历到最后一个指针是,并未判断Conditon(pNode) == SUCCESS是否成了,

而是直接返回了NULL指针,因此将while循环的条件改为pNode!=NULL,修改以后我们在测试一下,重新编译运行。发现已经解决了bug。

       

 三、callback接口

1.何为callback

      通俗的说就是通过函数指针调用的函数,定义如下:

      如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。

回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

2.实现机制:

       1.定义一个回调函数;
       2.提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者;

       3. 当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理。

3.回调函数的好处

        1.使得程序设计更加灵活。

        2.当我们想通过一个统一接口实现不同内容的时候,用回调函数来实现就非常合适。

        3.任何时候,如果你所编写的函数必须能够在不同的时刻执行不同的类型的工作或者执行只能由函数调用者定义的工作,你都

可以用回调函数来实现。

        4.使用此函数可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。它只需知道存在一个具有特定原型和限制条件的被

调用函数。简而言之,回调函数就是允许用户把需要调用的函数的指针作为参数传递给一个函数,以便该函数在处理相似事件的时候可以

灵活的使用不同的方法。

posted @ 2020-03-15 21:49  ustc_kunkun  阅读(234)  评论(0编辑  收藏  举报