• 工程化编程实战callback接口学习笔记(作业二)


    1. 在VSCode下编译运行lab5-1

    编译代码

    ERROR原因:menu.c中缺少了string类库,无法使用strcmp
    解决ERROR:menu.c中include <string.h>导入类库后,再进行编译

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

    使用help打印出所有的command,可以看出只有helpquit,挨个测试功能后发现quit不能按照期望输出

    step-2.1 排查错误原因(手工调试和使用GDB工具)

    step-2.2 手工法排查:

    排查错误过程:
    可以看到,输出This is a wrong cmd!是因为p是空指针,原因是FindCmd函数返回为空,查看FindCmd函数内容:

    FindCmd函数只有一个尾调用,执行SearchLinkTableNode(head,SearchCondition)函数,SearchLinkTableNode代码如下:

    可以看到,Conditon函数是一个回调函数。详解参考这篇文章。SearchLinkTableNode是call-in方式函数,其中有一个函数作为参数,这个作为参数的函数就是callback函数,即代码中Conditon函数。回调函数的实现方式及优点见文末。

    可以看到,SearchLinkTableNode为空的可能有:
    1.链表为空
    2.链表遍历完成后没有找到对应的command node
    由于help命令是可以正常运行的,所以只可能是quit对应的node没有正常被遍历,通过下图对添加cmmand节点的过程进行分析出


    quit node是添加在链表结尾的,每次添加新node时会把pNext置null,而SearchLinkTableNode函数中遍历链表时的条件为pNode != pLinkTable->pTail,显然没有判断到最后一个节点。所以,只需要把判断条件改为pNode != null后,重新编译运行,即可得到正确结果。

    step-2.3 使用GDB工具排查(过程已通过注释列出):

    xxx@xxx-virtual-machine:~/桌面/lab5-1/lab5.1$ gdb -q se2020
    Reading symbols from se2020...done.
    (gdb) list                                                             //列出menu.c中的内容
    89	    pNode->cmd = "quit";
    90	    pNode->desc = "Quit from Menu Program V1.0";
    91	    pNode->handler = Quit; 
    92	    AddLinkTableNode(*ppLinktable,(tLinkTableNode *)pNode);
    93	 
    94	    return 0; 
    95	}
    96	
    97	/* menu program */
    98	
    (gdb) 
    99	tLinkTable * head = NULL;
    100	
    101	int main()
    102	{
    103	    InitMenuData(&head); 
    104	   /* cmd line begins */
    105	    while(1)
    106	    {
    107	        printf("Input a cmd number > ");
    108	        scanf("%s", cmd);
    (gdb) 
    109	        tDataNode *p = FindCmd(head, cmd);
    110	        if( p == NULL)
    111	        {
    112	            printf("This is a wrong cmd!
     ");
    113	            continue;
    114	        }
    115	        printf("%s - %s
    ", p->cmd, p->desc); 
    116	        if(p->handler != NULL) 
    117	        { 
    118	            p->handler();
    (gdb) break 109												    //对109行的FindCmd函数加断点
    Breakpoint 1 at 0x400dea: file menu.c, line 109.
    (gdb) run													    //打完断点运行程序
    Starting program: /home/dfx/桌面/lab5-1/lab5.1/se2020 
    Input a cmd number > quit										//输入quit指令
    
    Breakpoint 1, main () at menu.c:109
    109	        tDataNode *p = FindCmd(head, cmd);
    (gdb) s														    //使用s进入FindCmd函数
    FindCmd (head=0x603010, cmd=0x6020a0 <cmd> "quit") at menu.c:60
    60	    return  (tDataNode*)SearchLinkTableNode(head,SearchCondition);
    (gdb) s												     //使用s进入SearchLinkTableNode函数
    SearchLinkTableNode (pLinkTable=0x603010, Conditon=0x400bf3 <SearchCondition>)
        at linktable.c:144
    144	    if(pLinkTable == NULL || Conditon == NULL)
    (gdb) n																//n为单行调式
    148	    tLinkTableNode * pNode = pLinkTable->pHead;
    (gdb) n
    149	    while(pNode != pLinkTable->pTail)
    (gdb) n
    151	        if(Conditon(pNode) == SUCCESS)
    (gdb) n
    155	        pNode = pNode->pNext;
    (gdb) n
    149	    while(pNode != pLinkTable->pTail)
    (gdb) n
    151	        if(Conditon(pNode) == SUCCESS)
    (gdb) n
    155	        pNode = pNode->pNext;
    (gdb) n
    149	    while(pNode != pLinkTable->pTail)
    (gdb) n	  //第三次while循环执行结束,由手工法也可以知道,初始化一共有三个节点,所以quit node没有被遍历
    157	    return NULL;
    (gdb) n
    158	}
    (gdb) n
    FindCmd (head=0x603010, cmd=0x6020a0 <cmd> "quit") at menu.c:61    //FindCmd返回
    61	}
    (gdb) n
    main () at menu.c:110
    110	        if( p == NULL)											//判断的quit cmd为null
    (gdb) n
    112	            printf("This is a wrong cmd!
     ");
    (gdb) n
    This is a wrong cmd!
    113	            continue;
    (gdb) 
    

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

    Definition:回调函数是异步执行或稍后执行的函数。其是通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数
    Example:有一家旅馆提供叫醒服务,但是要求旅客自己决定叫醒的方法。可以是打客房电话,也可以是派服务员去敲门,睡得死怕耽误事的,还可以要求往自己头上浇盆水。这里,“叫醒”这个行为是旅馆提供的,相当于库函数,但是叫醒的方式是由旅客决定并告诉旅馆的,也就是回调函数。
    Advantage:回调机制提供了非常大的灵活性,在回调中,我们利用某种方式,把回调函数像参数一样传入中间函数。可以这么理解,在传入一个回调函数之前,中间函数是不完整的。换句话说,程序可以在运行时,通过登记不同的回调函数,来决定、改变中间函数的行为。
    设计方法:回调机制是一种常见的设计模型,他把工作流内的某个功能,按照约定的接口暴露给外部使用者,为外部使用者提供数据,或要求外部使用者提供数据。比如java回调机制:
    软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。

    • 同步调用:一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用;
    • 回 调:一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;
    • 异步调用:一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。

    参考自

  • 相关阅读:
    python-列表生成式
    python-迭代
    python接口自动化测试-requests下载图片
    python接口自动化测试-requests请求异常处理
    python接口自动化测试-requests.get()
    什么是单元测试?如何做好单元测试?
    Elasticsearch 6 Mapping设置
    git pull报“unable to update local ref”解决方式
    Artifactory 简介
    maven项目版本管理
  • 原文地址:https://www.cnblogs.com/fxding/p/12503111.html
Copyright © 2020-2023  润新知