标签:cmd 函数 callback 编程 pNode gdb 调用 工程化 回调
一.VSCode下编译运行
通过如下编译命令
gcc -o test linktable.c menu.c
在当前工作目录下生成一个test.exe可执行文件
二.通过VSCode+GDB调试程序找出quit命令无法运行的bug产生的原因
源码分析
输入quit 发现提示错误命令wrong cmd
试着找出quit无法运行的原因,查看源码中menu.c中发现
tDataNode *p = FindCmd(head, cmd); if( p == NULL) { printf("This is a wrong cmd!\n "); continue; }
在p为null时,输出wrong cmd。接着查看FindCmd函数
/* find a cmd in the linklist and return the datanode pointer */ tDataNode* FindCmd(tLinkTable * head, char * cmd) { return (tDataNode*)SearchLinkTableNode(head,SearchCondition); }
FindCmd函数的返回值又由SearchLinkTableNode的返回值来决定。接着追根溯源查看SearchLinkTableNode函数
#int Conditon(tLinkTableNode * pNode)调用了回调函数
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; }
这段代码中。引入了一个回调函数的机制:该函数传入的第二个参数其实是一个函数指针,当我们使用这个函数指针所指向的函数时,此时它就被称为回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
通过阅读源码SearchLinkTableNode返回NULL指针的情况:
pNode循环遍历结束以后也未满足Conditon(pNode) == SUCCESS的条件
gdb调试工具分析
在49行添加断点
PS F:\SSEUSTC\高软\工程化编程实战callback接口学习笔记\lab5.1> gdb -q test Reading symbols from test...done. (gdb) break 49 Breakpoint 1 at 0x401974: file menu.c, line 49. (gdb) run Starting program: F:\SSEUSTC\\callback\lab5.1\test.exe [New Thread 11592.0x2f8c] [New Thread 11592.0x97c] [New Thread 11592.0x1bec] [New Thread 11592.0x2da8] warning: while parsing target library list: not well-formed (invalid token) Input a cmd number > quit Thread 1 hit Breakpoint 1, SearchCondition (pLinkTableNode=0xd81730) at menu.c:49 49 if(strcmp(pNode->cmd, cmd) == 0) (gdb) print pNode->cmd $1 = 0x405009 "help" (gdb) c Continuing. Thread 1 hit Breakpoint 1, SearchCondition (pLinkTableNode=0xd817d0) at menu.c:49 49 if(strcmp(pNode->cmd, cmd) == 0) (gdb) print pNode->cmd $2 = 0x405019 "version" (gdb) n 53 return FAILURE; (gdb) n 54 } (gdb) n SearchLinkTableNode (pLinkTable=0xd816e0, Conditon=0x401960 <SearchCondition>) at linktable.c:151 151 if(Conditon(pNode) == SUCCESS) (gdb) n 155 pNode = pNode->pNext; (gdb) n 149 while(pNode != pLinkTable->pTail) (gdb) print pNode $3 = (tLinkTableNode *) 0xd81820 (gdb) print pLinkTable->pTail $4 = (tLinkTableNode *) 0xd81820 (gdb) n 157 return NULL; (gdb) n 158 } (gdb) n FindCmd (head=0xd816e0, cmd=0x408980 <cmd> "quit") at menu.c:60 60 } (gdb) n main () at menu.c:109 109 if( p == NULL) (gdb) n 111 printf("This is a wrong cmd!\n "); (gdb) n This is a wrong cmd! 112 continue; (gdb)
上述红色部分可以发现:pNode->cmd=“quit”,链表中的尾结点没有参与比较,而且尾结点正是正确的quit命令。
因此将linktable.c文件中的while判断最后一个节点的逻辑改成while(pNode != NULL)
修改后运行结果如下:
三.分析callback接口的运行机制,总结callback接口设计的方法
回调函数定义
让人懵圈的百度百科的解释:
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
看上去不是那么容易理解,我们来看个例子(转自知乎):
你到一个商店买东西,刚好你要的东西没货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里:
你的电话号码就叫回调函数,
你把电话留给店员就叫登记回调函数,
店里后来有货了叫做触发了回调关联的事件,
店员给你打电话叫做调用回调函数,
你到店里去取货叫做响应回调事件。
什么时候使用回调函数
在分层设计中,高层次的模块会叫低层次的模块做一些事情,通常是通过函数调用。
从设计上来说,低层次的模块不应该直接调用高层次模块的函数。
所以高层次模块在叫低层模块做事的时候会注册一个回调函数给低层模块,然后低层模块做完了就调用这个函数。表现在C语言上是个函数指针。
本例中即是int Conditon(tLinkTableNode * pNode)
当程序跑起来时,一般情况下,应用程序(application program)会时常通过API调用库里所预先备好的函数。但是有些库函数(library function)却要求应用先传给它一个函数,好在合适的时候调用,以完成目标任务。这个被传入的、后又被调用的函数就称为回调函数(callback function)。
打个比方,有一家旅馆提供叫醒服务,但是要求旅客自己决定叫醒的方法。可以是打客房电话,也可以是派服务员去敲门,睡得死怕耽误事的,还可以要求往自己头上浇盆水。这里,“叫醒”这个行为是旅馆提供的,相当于库函数,但是叫醒的方式是由旅客决定并告诉旅馆的,也就是回调函数。而旅客告诉旅馆怎么叫醒自己的动作,也就是把回调函数传入库函数的动作,称为登记回调函数(to register a callback function)。如下图所示(图片来源:维基百科):
回调函数的好处
可以让实现方,根据回调方的多种形态进行不同的处理和操作。
可以让实现方,根据自己的需要定制回调方的不同形态。
可以将耗时的操作隐藏在回调方,不影响实现方其它信息的展示。
让代码的逻辑更加集中,更加易读。
标签:cmd,函数,callback,编程,pNode,gdb,调用,工程化,回调 来源: https://www.cnblogs.com/maizeDu/p/12502865.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。