工程化编程实战callback接口学习笔记

运行环境:macOS Catalina10.15.1 + VSCode1.42.1

 

一. 在VSCode下编译运行lab5-1.tar.gz 即http://pan.baidu.com/s/1pJ0qAIv

1. 使用gcc编译源代码,可执行文件命名为menu,编译器报出warning

2. 根据提示在menu.c中引入头文件 include <string.h> 解决这个warning,重新编译生成可执行文件menu

3. 运行menu,发现help、version命令正常,quit命令却提示“This is a wrong cmd!”

 

二. 通过VSCode+GDB调试程序找出quit命令无法运行的bug产生的原因 

1. 安装插件CodeLLDB,解决macos10.15下调试进程无法打断点的问题(断点处不停),参考 https://blog.csdn.net/mgsky1/article/details/103825710?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task。由于网速问题,可在github上手动下载codelldb-x86_64-darwin.vsix之后,通过快捷键Command+Shift+P,Extensions: Install from VSIX进行安装

2. 在menu.c中找到“This is a wrong cmd!”所在位置(line 111),可以看到它的判断条件是p==NULL, 而p的值又来自于line 108行的FindCmd函数,初步判定错误出在此函数

3. 找到FindCmd函数,发现它只有一行,即调用了SearchLinkTableNode函数,再次判定错误出在此函数的返回值

4. 找到SearchLinkTableNode函数,它在linktable.c里,并在三个return的位置打上断点

5. 分别在控制台输入 help、version 和 quit,发现输入前两个时,SearchLinkTableNode函数在line 153处返回,返回值为当前结点,而输入quit时函数在line157行返回NULL

6. 去掉line 146 和 line157行的断点,在line 149、line 151处打上新的断点,重新输入 quit 进行调试;发现在第三轮while循环时,由于pNode == ppLinkTable->pTail,本该在循环中被判断的pNode跳出循环,函数在line157返回了NULL。我们猜测可能是因为“quit”结点是pLinkTable的最后一个结点pTail,所以不止是它,只要是pLinkTable的最后一个结点就永远无法被正确执行。

7. 查看pLinkTable的定义,在main函数line 102,调用InitMenuData函数初始化ppLinktable时,链表ppLinktable第一个结点为“help”,第二个结点为“version”,第三个也就是最后一个结点为“quit”;印证了之前的猜测。

8.  我们把“version”和“quit”调换位置重新编译运行,即在上图中 1 2 3 -> 1 3 2, 发现如之前所料,help命令和quit命令正常,version命令报错;所以quit命令本身并没有任何问题,出现bug的原因仅因为while循环的退出条件有误。

9. 修改while循环的退出条件为 pNode != NULL 即可解决这个bug

 

三. 分析callback接口的运行机制,总结callback接口设计的方法

 1. 什么是回调函数(callback), call vs callback?

    call是直接调用,我调用系统函数,就是直调;

  callback是回调,我调用系统函数A,系统函数A又需要传入我的函数B作为参数,这种情况下,我并没有直接调用函数B,但是函数B还是被执行了,函数A调用函数B就是回调。

    A "callback" is any function that is called by another function which takes the first function as a parameter.

    简单来讲,回调函数是一个特殊的函数,它的名字(即指向函数地址的指针)作为参数被另一个函数调用。

2. 一个生动的例子(来自知乎https://www.zhihu.com/question/19801131/answer/13005983) 

    你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。
 
3. 在本实验中,main函数直接调用函数FindCmd,函数FindCmd直接调用函数SearchLinkTableNode;   
   函数SearchCondition作为参数被函数SearchLinkTableNode调用,此时SearchCondition就是一个回调函数; 函数SearchLinkTableNode可以通过传入不同的Condition函数来返回不同的结果。
   由此可以看到,相比普通的参数,回调函数为主程序和直接调用函数(中间函数)提供了一种更强大的交互模式,程序在运行时可以通过登记不同的回调函数来决定、改变中间函数的行为,这比简单的函数调用(直调)要灵活太多了;除此之外,在异步程序比如ajax请求中,往往需要等ajax执行完拿到数据才能继续执行后面的代码,此时使用回调函数尤为必要。
posted @ 2020-03-19 00:03  SA19225222  阅读(247)  评论(0编辑  收藏  举报