一、实验要求
-
在VSCode下编译运行lab5-1.tar.gz
-
通过VSCode+GDB调试程序找出quit命令无法运行的bug产生的原因
- 分析callback接口的运行机制,总结callback接口设计的方法
二、实验过程
1.首先安装好vscode和MinGW并配置好环境变量。
2.然后通过gcc -o test linktable.c menu.c 输入'quit'指令,发现执行quit命令无法退出程序,出现错误,具体如下:
3.检查menu.c的源码中“Input a cmd number”所在位置,从而找到下面代码:
while(1) 2 { 3 printf("Input a cmd number > "); 4 scanf("%s", cmd); 5 tDataNode *p = FindCmd(head, cmd); 6 if( p == NULL) 7 { 8 printf("This is a wrong cmd!\n "); 9 continue; 10 } 11 printf("%s - %s\n", p->cmd, p->desc); 12 if(p->handler != NULL) 13 { 14 p->handler(); 15 } 16 }
4.可以看到由于p为空指针,所以输出了错误信息,由于p指针是由FindCmd函数返回的,所以我们接着查看FindCmd函数。
FindCmd()函数如下:
tDataNode* FindCmd(tLinkTable * head, char * cmd) 2 { 3 return (tDataNode*)SearchLinkTableNode(head,SearchCondition); 4 }
5.由于其调用了SearchLinkTableNode()函数,所以继续查看。
SearchLinkTableNode()函数如下:
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode)) 6 { 7 if(pLinkTable == NULL || Conditon == NULL) 8 { 9 return NULL; 10 } 11 tLinkTableNode * pNode = pLinkTable->pHead; 12 while(pNode != pLinkTable->pTail) 13 { 14 if(Conditon(pNode) == SUCCESS) 15 { 16 return pNode; 17 } 18 pNode = pNode->pNext; 19 } 20 return NULL; 21 }
从中我们观察到返回NULL共有三个条件,具体如下:
(1) pLinkTable == NULL --- 不会出现该种情况
(2) Conditon == NULL --- 也不会出现该种情况
(3) pNode == pLinkTable->pTail --- 唯一出现NULL的可能
6.接下来需要知道的是pLinkTable->pTail是什么东西,具体如下:
LinkTable的数据结构:
typedef struct LinkTable { tLinkTableNode *pHead; tLinkTableNode *pTail; int SumOfNode; pthread_mutex_t mutex; }tLinkTable;
添加结点对pTail成员的处理如下:
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; } else { pLinkTable->pTail->pNext = pNode; pLinkTable->pTail = pNode; } pLinkTable->SumOfNode += 1 ; pthread_mutex_unlock(&(pLinkTable->mutex)); return SUCCESS; }
7.通过上述代码分析出:在链表添加代码时,pTail总会指向链表的最后一个节点。所以p == NULL是因为pNode指向链表的最后一个节点不满足循环条件,直接退出while循环。
8.因此我们只需要将while循环的条件改为pNode!=NULL,既可以结果之前的问题。
三、总结
1.回调函数:就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
2.回调函数的优点:
(1)使得程序设计更加灵活。
(2)当我们想通过一个统一接口实现不同内容的时候,用回调函数来实现就非常合适。
(3)任何时候,如果你所编写的函数必须能够在不同的时刻执行不同的类型的工作或者执行只能由函数调用者定义的工作,你都
可以用回调函数来实现。
(4)使用此函数可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。它只需知道存在一个具有特定原型和限制条件的被
调用函数。简而言之,回调函数就是允许用户把需要调用的函数的指针作为参数传递给一个函数,以便该函数在处理相似事件的时候可以
灵活的使用不同的方法。