作业要求
在VSCode下编译运行lab5-1.tar.gz 即http://pan.baidu.com/s/1pJ0qAIv
通过VSCode+GDB调试程序找出quit命令无法运行的bug产生的原因
分析callback接口的运行机制,总结callback接口设计的方法
具体实现
1. 环境准备
-
首先安装MAC环境下的GCC
-
打开VSCode,通过
Ctrl+Shift+x
安装C/C++调试器 -
下载作业要求中的lab5-1.tar.gz文件
2. 文件运行
- 打开VSCode命令行,使用如下命令编译程序,生成可执行文件
gcc -o test menu.c linktable.c
根据提示发现文件缺少函数strcmp所在的头文件,在menu.c中添加string.h头文件,继续编译后
- 编译成功,执行编译后文件,输入指令
./test
,进一步输入help和quit指令,结果如下:
3. 调试
- 定位main函数,代码如下:
int main()
{
InitMenuData(&head);
/* cmd line begins */
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();
}
}
}
发现通过调用FindCmd()
函数进行指令具体操作,进一步找到该函数的调用函数如下:
tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode))
{
if(pLinkTable == NULL || Conditon == NULL)
{
return NULL;
}
//部分代码
我们能够看到,调用该查找函数传入的参数为InitMenuData(&head)
链表和我们输入的cmd命令,我们来检查一下直接返回空指针只有在pLinkTable和Conditon至少有一个为空的时候,很明显这是我们的输入和cmd命令,并不为空,让我们继续看另外一种情况
//承接上部分代码
while(pNode != pLinkTable->pTail)
{
if(Conditon(pNode) == SUCCESS)
{
return pNode;
}
pNode = pNode->pNext;
}
return NULL;
}
另一种情况就是直到pNode循环遍历结束以后也未满足Conditon(pNode) == SUCCESS的条件,那么我们来看看pLinkTable->pTai
else
{
pLinkTable->pTail->pNext = pNode;
pLinkTable->pTail = pNode;
}
pLinkTable->SumOfNode += 1 ;
pthread_mutex_unlock(&(pLinkTable->mutex));
return SUCCESS;
不难发现,在判断是否为末尾的时候并未判断Conditon(pNode) == SUCCESS,而直接返回空指针,我们将SearchLinkTableNode函数修改如下(改为判断只有不为空的时候才进行遍历):
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接口
当程序跑起来时,一般情况下,应用程序(application program)会时常通过API调用库里所预先备好的函数。但是有些库函数(library function)却要求应用先传给它一个函数,好在合适的时候调用,以完成目标任务。这个被传入的、后又被调用的函数就称为回调函数(callback function)。
可以看到,回调函数通常和应用处于同一抽象层(因为传入什么样的回调函数是在应用级别决定的)。而回调就成了一个高层调用底层,底层再回过头来调用高层的过程。