一、实验要求
-
在VSCode下编译运行lab5-1.tar.gz
-
通过VSCode+GDB调试程序找出quit命令无法运行的bug产生的原因
-
分析callback接口的运行机制,总结callback接口设计的方法
二、实验过程
1、 为了能在VSCode运行代码,需要VSCode扩展商店中安装C++/C和Code Runner,并下载配置mingw-64 配置教程,cmd输入gcc/g++ -v查看mingw是否安装成功
2、 通过在终端输入gcc -o test linktable.c menu.c
生成可执行文件test.exe
3、 运行test.exe输入help和quit得到以下结果
4、 检查menu.c的源码,发现当p指针为空时返回 This is a wrong cmd!
在109行打上断点,使用gdb调试
5、 可以看到FindCmd又调用了SearchLinkTableNode函数,该函数如下
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;
}
6、 显然输入quit使得SerchLinkTableNode函数返回了一个空指针,则可能是pLinkTable == NULL或Conditon == NULL或者直到pNode循环遍历结束以后也未满足Conditon(pNode) == SUCCESS的条件,显然前两种情况不符合,查看循环条件的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;
}
else
{
pLinkTable->pTail->pNext = pNode;
pLinkTable->pTail = pNode;
}
pLinkTable->SumOfNode += 1 ;
pthread_mutex_unlock(&(pLinkTable->mutex));
return SUCCESS;
}
7、 调试发现,当pNode遍历到最后一个指针时,没有判断Conditon(pNode) == SUCCESS是否成功,而是直接返回了NULL指针,要使程序能够正常运行就需要修改SearchLinkTableNode函数中while的判断条件使其能够访问链表中的最后一个元素,将循环条件进行修改,使其能遍历每一个节点:
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode))
{
if(pLinkTable == NULL || Conditon == NULL)
{
return NULL;
}
tLinkTableNode * pNode = pLinkTable->pHead;
while(pNode != null) //修改后,遍历每一个节点
{
if(Conditon(pNode) == SUCCESS)
{
return pNode;
}
pNode = pNode->pNext;
}
return NULL;
}
三、总结
回调函数是把函数的指针作为参数传递给另一个函数,callback函数的调用实质上是在一个模块内部调用模块外部的另一个模块实现的函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。说白了就是A调用了B,B在适当的时候又反回去调用A。这样的东西都可以叫做回调函数。从上面也可以看出来,多数时候因为是单线程,A没有必要等B来调用它,因为A在调用完B之后完全可以调用自己需要的操作。所以回调多见于事件驱动机制里。