2017 Fall Software Engineering Learning (5)

2017 Fall Software Engineering Learning (5)

用callback增强链表模块 命令行菜单

公开发表一篇实验报告,并在实验报告中注明【网易云课堂昵称 + 《软件工程(C编码实践篇)》MOOC课程作业http://mooc.study.163.com/course/USTC-1000002006


一、实验需求分析

  • 给lab5-1.tar.gz找bug,quit命令无法运行的bug
  • 利用callback函数参数使Linktable的查询接口更加通用
  • 注意接口的信息隐藏

二、程序排错

这次的题目相对来说轻松许多,因为不用自己写代码,只用在给的模板上进行修改。
首先是给程序找bug,这个bug其实很容易找,其他命令都没错,只有quit这个命令出错,而quit刚好位于尾节点。同时我们发现,使用help命令时,能够打印quit这个节点的介绍,这意味着程序并不是在创建链表的时候出现的错误。那就意味着肯定是在搜索命令的时候出错了,我们找到搜索函数:

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;
}

这里写图片描述
我们发现,循环的判断语句是pNode == 链表尾节点的时候退出,那么在pNode指向尾节点的时候并没有执行循环内的操作,所以就会返回NULL,这样,尾节点的命令就没办法找到了。所以我们稍作修改:

tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode,void *arguments),void *arguments)
{
    if(pLinkTable == NULL || Conditon == NULL)
    {
        return NULL;
    }
    tLinkTableNode * pNode = pLinkTable->pHead;
    while(1)
    {    
        if(Conditon(pNode,arguments) == SUCCESS)
        {
            return pNode;                   
        }
        if (pNode == pLinkTable->pTail)
            break;
        pNode = pNode->pNext;
    }
    return NULL;
}

先执行循环,再执行判断操作。原始但是管用的操作。
这里写图片描述

三、程序接口通用化和隐藏

这个就像老师视频中所说的,用callback来通用化接口,也就是用函数指针来作为参数。优点在于连接口具体内容都可以用户自定义,接口变得更为抽象。
关键代码:

tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode,void *arguments),void *arguments)
{
    if(pLinkTable == NULL || Conditon == NULL)
    {
        return NULL;
    }
    tLinkTableNode * pNode = pLinkTable->pHead;
    while(1)
    {    
        if(Conditon(pNode,arguments) == SUCCESS)
        {
            return pNode;                   
        }
        if (pNode == pLinkTable->pTail)
            break;
        pNode = pNode->pNext;
    }
    return NULL;
}
int SearchCondition(tLinkTableNode * pLinkTableNode,void *arguments)
{
    tDataNode * pNode = (tDataNode *)pLinkTableNode;
    char *cmd = (char*)arguments; 
    if(strcmp(pNode->cmd, cmd) == 0)
    {
        return  SUCCESS;  
    }
    return FAILURE;        
}

以上是函数定义,我们将SearchCondition()函数作为接口参数给传到SearchLinkTableNode()函数中,具体代码:

tDataNode* FindCmd(tLinkTable * head, char * cmd)
{
    return  (tDataNode*)SearchLinkTableNode(head,SearchCondition,cmd);
}
tDataNode *p = FindCmd(head, cmd);

这种实现方法非常好,不过它作为一种思想,我也没什么其他可以介绍的了,记住这种好方法就是了。
再就是根据实验说的,将结构体具体内容放到.c文件里,因为.h是暴露给用户的,对于用户要尽量少的暴露具体细节。

四、实验总结

这次实验工作量不大,但是内容非常有意义,着重介绍了可重用的一个小细节,接口的通用性。而且通过这个代码的阅读,我发现了许多我原本程序的不足,尤其是我感觉到了链表的各种具体实现上的区别,还有将功能独立的代码模块封装成一个函数这些思想,而不是仅仅只在于功能文件化。

五、结果展示

这次因为每有什么独立编程的内容,只是修改代码,所以没什么好展示的:
这里写图片描述
各个功能模块正常,没啥其他的。
重点在于代码!!!这是我的代码,有兴趣可以去看看


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值