[高级软件工程实验]将menu设计为可重用的子系统

来源:互联网 发布:好喝的洋酒推荐 知乎 编辑:程序博客网 时间:2024/04/20 19:48

版本库URL:https://github.com/swagnhen/Advanced-Software-Engineering-Exercise.git

实验要求

1)为menu子系统设计接口,并写用户范例代码来实现原来的功能;2)使用make和make clean来编译程序和清理自动生成的文件;3)使menu子系统支持带参数的复杂命令,并在用户范例代码中自定义一个带参数的复杂命令;4)可以使用getopt函数获取命令行参数。

实验内容

callback增强可重用链表实现包含于linkedlist.h与linkedlist.c中
shell中各命令功能的实现包含在cmdopt.h与cmdopt.c中
main函数包含于shelllet.c中
编译时请使用

make

1.子系统设计接口

//用于用户向命令行添加新的命令功能int ShellletConfig(LinkedList *head, char * cmd, int (*handler)());//用于启动命令行程序int ExecuteShelllet(LinkedList *head);

具体实现

int ShellletConfig(LinkedList *l, char *cmd, int (*handler)()){    CmdNode *p = NULL;    if (l == NULL)    {        return -1;    }    p = (CmdNode *)malloc(sizeof(CmdNode));    p->cmd = cmd;    p->handler = handler;    addNode(l, (Node *)p);    return 0;}int ExecuteShelllet(LinkedList *l){    int argc = 0;    char *argv[32];    printf("**********Shelllet Running**********\n");    while (1)    {        printf(">>>");        //cmd2arg函数用于将输入的命令转化为argc与argv形式        cmd2arg(&argc, argv);        CmdNode *p = findCmd(l, argv[0]);        if (p != NULL && p->handler != NULL)            p->handler(argc, argv);        argc = 0;    }    return 0;}

2.Makefile编译文件

## Makefile for Shelllet Program#CC_PTHREAD_FLAGS             = -lpthreadCC_FLAGS                     = -c CC_OUTPUT_FLAGS              = -oCC                           = gccRM                           = rmRM_FLAGS                     = -fTARGET  =   testOBJS    =   linkedlist.o  cmdopt.o shelllet.oall:    $(OBJS)    $(CC) $(CC_OUTPUT_FLAGS) $(TARGET) $(OBJS) .c.o:    $(CC) $(CC_FLAGS) $<clean:    $(RM) $(RM_FLAGS)  $(OBJS) $(TARGET) *.bak

3.复杂指令解析

void cmd2arg(int *argc, char **argv){    char cmd[1024];    char *pcmd = NULL;    pcmd = fgets(cmd, 1024, stdin);    if (pcmd == NULL)        return;    pcmd = strtok(pcmd, " ");    while (pcmd != NULL && *argc < 32)    {        argv[*argc] = pcmd;        (*argc)++;        pcmd = strtok(NULL, " ");    }    if (*argc == 1)    {        int len = strlen(argv[0]);        *(argv[0] + len - 1) = '\0';    }}

用户实现的复杂指令函数

int test(int argc, char *argv[]){    printf(">>>Test success\n");    return 0;}

4.获取命令行参

经cmd2arg处理后argv[1]开始均为命令行参数

void cmd2arg(int *argc, char **argv){    char cmd[1024];    char *pcmd = NULL;    pcmd = fgets(cmd, 1024, stdin);    if (pcmd == NULL)        return;    pcmd = strtok(pcmd, " ");    while (pcmd != NULL && *argc < 32)    {        argv[*argc] = pcmd;        (*argc)++;        pcmd = strtok(NULL, " ");    }    if (*argc == 1)    {        int len = strlen(argv[0]);        *(argv[0] + len - 1) = '\0';    }}

运行效果
这里写图片描述

实验总结

我对老师的写法做了一些修改,还是去掉了全局链表,这样的话每次用户(程序员)在使用这个程序的时候都得自己先建立一个链表,初始化之后再将链表传入ExecuteShelllet函数中执行命令行程序。
这样应该算是面向对象的实现思路吧,毕竟C中没有类,不能定义一个Menu类再向其添加start方法。
其实这个程序有bug,不论输什么命令第一次都会失效。
我调试之后发现是在第一次解析命令时字符串比对函数strcmp出现错误,没有返回正确结果的同时还改变了形参cmd的值。
就很神秘,这个函数的原型不是加了const么,怎么还能改变形参值的,还只出现一次。
感觉可能是头文件include的问题,之前在没加string.h的时候函数strtok也出现了一样的问题。
试了很多没有解决,就搁置了。
还有就是像加减乘除这样有指令执行中含有scanf调用的命令2,运行结束后程序会执行一个空指令(即“”)。
这个肯定是fget和scanf混用的问题,估计是add运行结束之后有个换行符\n还在输入流里,下一轮开始的时候被fget读到了。
虽然在每个scanf后面单独加个fget应该能解决,但是感觉这种方式非常蠢,也没想到更好的方式,就算了。
这程序再拓展几次估计就要不能维护了。

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

阅读全文
0 0
原创粉丝点击