“软件工程(C编码实践篇)”实验报告
实验七:将menu设计为可重用的子系统
网易云课堂昵称:
Arjen0130
《软件工程(C编码实践篇)》MOOC课程作业
http://mooc.study.163.com/course/USTC-1000002006
依照学术诚信条款,我保证此回答为本人原创,所有回答中引用的外部材料已经做了出处标记。
实验报告原链接地址:http://note.youdao.com/noteshare?id=c18689570e3e66ecc2d226d2603e1beb&sub=ADEC9A84FF5C4FEBB80D28D2A701F643
1. 实验内容和要求
1.1 实验内容
在实验5的代码的基础上,将menu设计为可重用的子系统
1.2 实验要求
- 为menu子系统设计接口,并写用户范例代码来实现原来的功能;
- 使用make和make clean来编译程序和清理自动生成的文件;
- 使menu子系统支持带参数的复杂命令,并在用户范例代码中自定义一个带参数的复杂命令;
- 可以使用getopt函数获取命令行参数。
注:
本实验在实验5的代码基础上进行。
2. 实验的思路和具体过程
2.1 实验的思路
看完实验七的相关学习视频以后,学习到了将一个menu系统设计为可重用子系统的方法,特别是相关接口的定义方法。
依照视频讲解,并参照实验要求,从而完成本次实验。
2.2 实验的具体过程
1)使用实验1中创建好的本地仓库,添加lab7文件夹,把实验5的需要用到的文件复制到lab7文件夹中;
2)添加接口文件,并对已有的相关文件进行修改,使其能够作为可重用的子系统被调用;
3)添加测试文件,测试步骤2)中完成的可重用的子系统的相关功能是否正确;
4)编译、调试通过后,添加到git本地仓库,并上传到git远端仓库。
3. 关键代码
3.1 增加的接口文件menu.h
... ...
/*
* Add cmd to menu.
* @cmd: Name of the cmd to be added.
* @desc: Description of the cmd to be added.
* @handler: A function handler which executes the cmd.
* @return: Number reflects function status.
*/
int MenuConfig(char * cmd, char * desc, int (*handler)(int argc, char * argv[]));
/*
* Menu Engine Execute.
* @return: Number reflects function status.
*/
int ExecuteMenu();
3.2 对menu.c文件做出的修改
... ...
#include "menu.h"
int Help(int argc, char * argv[]);
#define CMD_MAX_LEN 128
#define CMD_MAX_ARGV_NUM 10
#define DESC_LEN 1024
#define CMD_NUM 10
//char cmd[CMD_MAX_LEN];
/* data struct and its operations */
typedef struct DataNode
{
tLinkTableNode * pNext;
char* cmd;
char* desc;
int (*handler)(int argc, char * argv[]);
} tDataNode;
... ...
/*
* Add cmd to menu.
* @cmd: Name of the cmd to be added.
* @desc: Description of the cmd to be added.
* @handler: A function handler which executes the cmd.
* @return: Number reflects function status.
*/
int MenuConfig(char * cmd, char * desc, int (*handler)(int argc, char * argv[]))
{
tDataNode * pNode = NULL;
if(NULL == head)
{
head = CreateLinkTable();
pNode = (tDataNode *)malloc(sizeof(tDataNode));
pNode->cmd = "help";
pNode->desc = "Menu List:";
pNode->handler = Help;
AddLinkTableNode(head, (tLinkTableNode *)pNode);
}
pNode = (tDataNode *)malloc(sizeof(tDataNode));
pNode->cmd = cmd;
pNode->desc = desc;
pNode->handler = handler;
AddLinkTableNode(head, (tLinkTableNode *)pNode);
return 0;
}
/*
* Menu Engine Execute.
* @return: Number reflects function status.
*/
int ExecuteMenu()
{
/* cmd line begins */
while(1)
{
int argc = 0;
char *argv[CMD_MAX_ARGV_NUM];
char cmd[CMD_MAX_LEN];
char * pcmd = NULL;
printf("Input a cmd > ");
/* scanf("%s", cmd); */
pcmd = fgets(cmd, CMD_MAX_LEN, stdin);
if(NULL == pcmd)
{
continue;
}
/* convert cmd to argc/argv */
pcmd = strtok(pcmd, " ");
while((NULL != pcmd) && (CMD_MAX_ARGV_NUM > argc))
{
argv[argc++] = pcmd;
pcmd = strtok(NULL, " ");
}
if(1 == argc)
{
int len = strlen(argv[0]);
*(argv[0] + len - 1) = '\0';
}
tDataNode *p = FindCmd(head, argv[0]);
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(argc, argv);
}
}
}
int Help(int argc, char * argv[])
{
ShowAllCmd(head);
return 0;
}
... ...
3.3 增加的测试文件test.c
... ...
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include "menu.h"
#define MAX_OPTION_ARGUMENT_SIZE 128
/*
*This function is used to process the "compare" command.
*/
int compare(int argc, char * argv[])
{
int ch;
char argA[MAX_OPTION_ARGUMENT_SIZE];
char argB[MAX_OPTION_ARGUMENT_SIZE];
printf("This is the compare command...\n");
/* for(int i = 0; i < argc; i++) */
/* { */
/* printf("The argv[%d] = %s\n", i, argv[i]); */
/* } */
/* printf("optind: %d\n", optind); */
optind = 1; //这句必不可少,否则,多次执行相同的参数时,会出现结果不一致的情
while((ch = getopt(argc, argv, "a:b:")) != -1)
{
printf("optind: %d\n", optind);
switch(ch)
{
case 'a':
printf("HAVE option: -a\n");
printf("The argument of -a is %s\n", optarg);
argA[0] = '\0';
if(NULL != optarg)
{
memcpy(argA, optarg, strlen(optarg));
}
break;
case 'b':
printf("HAVE option: -b\n");
printf("The argument of -b is %s\n", optarg);
argB[0] = '\0';
if(NULL != optarg)
{
memcpy(argB, optarg, strlen(optarg));
}
break;
case '?':
printf("Unknown option: %c\n", (char)optopt);
break;
}
}
return 0;
}
/*
*This function is used to process the "get" command.
*/
int get(int argc, char * argv[])
{
printf("This is the get command...\n");
return 0;
}
/*
*This function is used to process the "pull" command.
*/
int pull(int argc, char * argv[])
{
printf("This is the pull command...\n");
return 0;
}
/*
*This function is used to process the "push" command.
*/
int push(int argc, char * argv[])
{
printf("This is the push command...\n");
return 0;
}
/*
*This function is used to process the "put" command.
*/
int put(int argc, char * argv[])
{
printf("This is the put command...\n");
return 0;
}
/*
*This function is used to quit the program.
*/
int quit(int argc, char * argv[])
{
printf("This is the quit command...\n");
exit(0);
}
int main(int argc, int * argv[])
{
MenuConfig("compare", "compare cmd:", compare);
MenuConfig("get", "get cmd:", get);
MenuConfig("pull", "pull cmd:", pull);
MenuConfig("push", "push cmd:", push);
MenuConfig("put", "put cmd:", put);
MenuConfig("quit", "quit Cmd:", quit);
return ExecuteMenu();
}
3.4 Makefile文件代码
#
# Makefile for Menu Program
#
CC_PTHREAD_FLAGS = -lpthread
CC_FLAGS = -c
CC_OUTPUT_FLAGS = -o
CC = gcc
RM = rm
RM_FLAGS = -f
TARGET = test
OBJS = test.o menu.o linktable.o
all: $(OBJS)
$(CC) $(CC_OUTPUT_FLAGS) $(TARGET) $(OBJS)
.c.o:
$(CC) $(CC_FLAGS) $<
clean:
$(RM) $(RM_FLAGS) $(OBJS) $(TARGET) *.bak
4. 相关截图
4.1 实验结果截图
4.2 关键代码截图
4.2.1 test.c文件中的关键代码截图
4.2.2 menu.h文件中的关键代码截图
4.2.3 menu.c文件中的关键代码截图
4.2.4 Makefile文件截图
4.3 操作过程截图
4.3.1 本次实验用到的头文件和源文件
4.3.2 执行make命令后的结果
4.3.3 将本地仓库的变化提交到远端仓库
4.4 复现操作截图
5. 实验过程中遇到的疑惑、困难及处理方法
本次实验过程中遇到的最大的问题就是getopt函数的调用问题。对应的menu命令初次执行的之后,一切正常,getopt函数可以正确读出menu命令的选项和参数。但是,当重复执行时,该函数无法正常工作。经过查找相关资料,发现getopt函数的使用了几个全局变量,其中一个optind变量用来记录上一次的处理位置。实际结果表明,为了使同一个命令能够多次重复工作,在调用getopt函数之前,需要重置optind变量的值。
6. 实验总结
通过本次实验,学习到了将一个menu程序修改为可重入子系统的方法。并且,学习使用了getopt函数。