实验要求
-
在VSCode下编译运行lab5-1.tar.gz 即http://pan.baidu.com/s/1pJ0qAIv
-
通过VSCode+GDB调试程序找出quit命令无法运行的bug产生的原因
- 分析callback接口的运行机制,总结callback接口设计的方法
一 配置环境
1 安装gcc
首先在Windows下的VSCode下搭建C开发环境,这里使用mingw-w64配置gcc环境,文件链接:https://sourceforge.net/projects/mingw-w64/
将文件解压,我这里放的是C:\Program Files\mingw-w64\MinGW\bin文件夹
2 在系统变量中的Path添加环境变量
打开命令行窗口,输入gcc -v看见gcc版本相关信息,安装完成
3 安装VSCode,并安装C/C++调试器
环境配置完成
二 用VSCode运行示例代码
在终端输入:
gcc -o test menu.c linktable.c
提示有两个错误
错误1:缺少函数strcmp头文件string.h声明,在menu.c中添加string.h头文件
错误2:缺少pthread头文件,该问题比较复杂,具体解决方法参考https://blog.csdn.net/CSDN_WHB/article/details/81475233
添加完pthread库后在终端输入:
gcc -o test menu.c linktable.c -lpthread
成功生成目标程序
输入./test运行程序,输入help,quit指令
Quit命令提示是一个错误的指令。
三 通过VSCode+GDB调试程序找出quit命令无法运行的bug产生的原因
找到main输入指令位置,cmd是一个全局变量,由这里我们可以知道p为空,所以会错误,
继续查看FindCmd函数,这个函数是通过给定的链表和cmd值返回数据节点的地址,函数代码如下
该函数调用了SearchLinkTableNode,继续查看SearchLinkTableNode函数
该函数的实现大概是,从链表头开始遍历,对比链表节点,找到符合节点就返回该节点指针,失败则返回null
这里链表不为空且condition函数存在,应该是循环结束没有返回节点,执行最后的return null语句
仔细分析循环体:while(pNode != pLinkTable->pTail)该循环条件表示节点不为表尾则继续循环,但是我们查看InitMenuData(tLinkTable ** ppLinktable)函数发现,quit节点刚好就是链表的尾,所以遍历到该节点时没有访问就跳出循环了,因此这里返回了null
我们将判断条件改为
while(pNode != NULL)
重新执行,程序成功运行
四 分析callback接口的运行机制,总结callback接口设计的方法
1 回调函数的运行机制
回调函数是指使用者自己定义一个函数,实现这个函数的程序内容,然后把这个函数(入口地址)作为参数传入别人(或系统)的函数中,由别人(或系统)的函数在运行时来调用的函数。函数是你实现的,但由别人(或系统)的函数在运行时通过参数传递的方式调用,这就是所谓的回调函数。简单来说,就是由别人的函数运行期间来回调你实现的函数。
这一设计允许了底层代码调用在高层定义的子程序。C语言中回调函数主要通过函数指针的方式实现。
本例中使用了回调函数的机制,本例中FindCmd(tLinkTable * head, char * cmd)调用SearchLinkTableNode(head,SearchCondition)函数,而SearchLinkTableNode通过调用了回调函数condition()。实现函数调用中,函数调用了“调用函数”,再在其中进一步调用被“调用函数”。相比于主函数直接调用“被调函数”,这种方法为使用者,而不是开发者提供了灵活的接口。另外,函数入口可以像变量一样设定同样为开发者提供了灵活性。
2 Callback接口设计的方法
- 回调函数传入的参数是函数指针,函数指针用来调用其所指向的函数,调用者和被调用者分开,调用者并不关心谁是被调用者,可以对特定的事件或条件进行响应,当发生某种事件时系统或其他函数会自动调用定义的一段函数。
- 将符合要求的参数以及函数指针作为调用函数的参数。当用户调用时,只需要将函数指针指向不同的函数即可