一、实验目的
-
在VSCode下编译运行lab5-1.tar.gz 即http://pan.baidu.com/s/1pJ0qAIv
-
通过VSCode+GDB调试程序找出quit命令无法运行的bug产生的原因
- 分析callback接口的运行机制,总结callback接口设计的方法
二、实验过程
1、运行menu.c,找出错误的运行结果。
运行结果为:
查看结果,使用quit发现没有退出程序,而是提示worng cmd,显然是错误的。
2、查看源文件中“Input a cmd number”所在位置,从而定位以下代码
1 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 }
观察代码可知,输出“wrong cmd”的判断语句是p与NULL的关系,指针p指向空,导致quit命令输出结果错误。
而观察第5行可知,指针p是FindCmd()函数的返回值。
3、观察FindCmd()函数。
1 tDataNode* FindCmd(tLinkTable * head, char * cmd) 2 { 3 return (tDataNode*)SearchLinkTableNode(head,SearchCondition); 4 }
发现其调用SearchLinkTableNode()函数。
1 /* 2 * Search a LinkTableNode from LinkTable 3 * int Conditon(tLinkTableNode * pNode); 4 */ 5 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 }
从SearchLinkTableNode()源码可以看出:
返回NULL的条件有三个:
(1) pLinkTable == NULL --- 不会出现该种情况
(2) Conditon == NULL --- 也不会出现该种情况
(3) pNode == pLinkTable->pTail --- 唯一出现NULL的可能
4、pTail是什么?
观察结构体:
添加结点时对pTail成员的处理:
1 /* 2 * Add a LinkTableNode to LinkTable 3 */ 4 int AddLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode) 5 { 6 if(pLinkTable == NULL || pNode == NULL) 7 { 8 return FAILURE; 9 } 10 pNode->pNext = NULL; 11 pthread_mutex_lock(&(pLinkTable->mutex)); 12 if(pLinkTable->pHead == NULL) 13 { 14 pLinkTable->pHead = pNode; 15 } 16 if(pLinkTable->pTail == NULL) 17 { 18 pLinkTable->pTail = pNode; 19 } 20 else 21 { 22 pLinkTable->pTail->pNext = pNode; 23 pLinkTable->pTail = pNode; 24 } 25 pLinkTable->SumOfNode += 1 ; 26 pthread_mutex_unlock(&(pLinkTable->mutex)); 27 return SUCCESS; 28 }
由上可知:在链表添加代码时,pTail总会指向链表的最后一个节点。所以p == NULL是因为pNode指向链表的最后一个节点不满足循环条件,直接退出while循环。
while循环条件while(pNode != pLinkTable -> pTail)使得函数在找到链表最后一个节点时即退出循环,因此无法访问最后一个节点,而退出的话需要找到最后的节点,所以我们可以将错误代码while(pNode != pLinkTable -> pTail)改为while(pNode != NULL)来解决bug。
三、总结
回调函数:就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。