您的位置:首页 > 其它

软件工程课程实验报告:实验五

2017-10-23 21:43 176 查看

实验五:用callback增强链表模块来实现命令行菜单小程序V2.8

咖啡机《软件工程(C编码实践篇)》MOOC课程作业http://mooc.study.163.com/course/USTC-1000002006

新创建一个目录lab5完成实验。

然后将lab5-1.tar.gz中的代码(即解压后lab5.1/目录下的源文件)直接放到lab5/目录下继续完成后面的实验内容。

一、实验要求

给lab5-1.tar.gz找bug,quit命令无法运行的bug

利用callback函数参数使Linktable的查询接口更加通用

注意接口的信息隐藏

及时提交代码以防丢失

二、实验过程

1. 创建lab5文件夹,将lab5-1.tar.gz解压至文件夹下



2. 编译代码并执行,发现
quit()
命令无法正确执行




3. 从程序的主入口入手,进行代码审查

menu.c中的
main()
函数及
InitMenuData(tLinkTable ** ppLinktable)
函数

int InitMenuData(tLinkTable ** ppLinktable)
{
*ppLinktable = CreateLinkTable();
tDataNode* pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "help";
pNode->desc = "Menu List:";
pNode->handler = Help;
AddLinkTableNode(*ppLinktable,(tLinkTableNode *)pNode);
pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "version";
pNode->desc = "Menu Program V1.0";
pNode->handler = NULL;
AddLinkTableNode(*ppLinktable,(tLinkTable
4000
Node *)pNode);
pNode = (tDataNode*)malloc(sizeof(tDataNode));
pNode->cmd = "quit";
pNode->desc = "Quit from Menu Program V1.0";
pNode->handler = Quit;
AddLinkTableNode(*ppLinktable,(tLinkTableNode *)pNode);

return 0;
}

/* menu program */

tLinkTable * head = NULL;

int main()
{
InitMenuData(&head);
/* cmd line begins */
while(1)
{
printf("Input a cmd number > ");
scanf("%s", cmd);
tDataNode *p = FindCmd(head, cmd);
if( p == NULL)
{
printf("This is a wrong cmd!\n ");
continue;
}
printf("%s - %s\n", p->cmd, p->desc);
if(p->handler != NULL)
{
p->handler();
}

}
}


可以看到,在
main()
中,先初始化命令链表,
InitMenuData(tLinkTable ** ppLinktable)
将quit命令的cmd和desc属性赋值。按照
main()
的正常运行过程,输入quit,程序的输出应为”Quit from Menu Program v1.0”。

main()
中初始化命令链表之后,便是一个用来等待用户输入命令、并进行相关处理的循环体。在循环中,注意到输入的cmd通过
FindCmd()
函数查找命令链表对应的结点p,p不为空时输出结点,p为空时提示错误”This is a Wrong cmd”,这恰与我们目前程序quit的输出相同。因此,错误可能来源于
FindCmd()
函数。

- menu.c中的
FindCmd()
SearchCondition()
函数

int SearchCondition(tLinkTableNode * pLinkTableNode)
{
tDataNode * pNode = (tDataNode *)pLinkTableNode;
if(strcmp(pNode->cmd, cmd) == 0)
{
return  SUCCESS;
}
return FAILURE;
}

/* find a cmd in the linklist and return the datanode pointer */
tDataNode* FindCmd(tLinkTable * head, char * cmd)
{
return  (tDataNode*)SearchLinkTableNode(head,SearchCondition);
}


FindCmd()
中可以看到,调用了linktable.c中的
SearchLinkTableNode()
函数,其代码如下:

/*
* Search a LinkTableNode from LinkTable
* 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;
}


函数中的循环体循环条件
while(pNode != pLinkTable->pTail)
使得函数在到达命令链表尾部时,无法退出循环,无法访问最后一个结点。在这个程序中,最后一个结点就是quit命令。因此作以下修改(见代码中注释):

/*
* Search a LinkTableNode from LinkTable
* 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 != NULL)
{
if(Conditon(pNode) == SUCCESS)
{
return pNode;
}
pNode = pNode->pNext;
}
return NULL;
}


3. 再次编译运行,发现bug消失



4. 解决数据结构的通用型

menu.c中

将全局变量cmd[CMD_MAX_LEN]放入
main()
函数内

int main()
{
InitMenuData(&head);
//降低模块间的耦合
char cmd[CMD_MAX_LEN];
/* cmd line begins */
while(1)
{
printf("Input a cmd number > ");
scanf("%s", cmd);
tDataNode *p = FindCmd(head, cmd);
if( p == NULL)
{
printf("This is a wrong cmd!\n ");
continue;
}
printf("%s - %s\n", p->cmd, p->desc);
if(p->handler != NULL)
{
p->handler();
}

}
}


修改
SearchCondition()
的参数,传入cmd变量。

int SearchCondition(tLinkTableNode * pLinkTableNode,void * args)
{
char *cmd = (char *)args;
tDataNode * pNode = (tDataNode *)pLinkTableNode;
if(strcmp(pNode->cmd, cmd) == 0)
{
return  SUCCESS;
}
return FAILURE;
}


修改
FindCmd()
函数:

/* find a cmd in the linklist and return the datanode pointer */
tDataNode* FindCmd(tLinkTable * head, char * cmd)
{
return  (tDataNode*)SearchLinkTableNode(head, SearchCondition, (void *) cmd);
}


在linktable.c中修改
SearchLinkTableNode()
函数:

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


5. 隐藏重要数据结构实现

linktable.h中

在linktable.h接只提供结构体类型的声明,将结构体类型的定义放在linktable.c中。

/*
* LinkTable Node Type
*/
typedef struct LinkTableNode tLinkTableNode;

/*
* LinkTable Type
*/
typedef struct LinkTable tLinkTable;


6. 编译运行



7. 将代码同步到github



Github地址:https://github.com/973301529/se/tree/master/lab5

三、实验总结

通过本次实验,软件开发过程中需要具备模块化思想,掌握了接口函数的作用。对于callback函数进行了简单的实践,体会到了它的优势。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: