软件工程(C编码实践篇)学习总结

来源:互联网 发布:淘宝店怎么经营管理 编辑:程序博客网 时间:2024/06/18 09:38

刘思琪 + 原创作品转载请注明出处 + 《软件工程(C编码实践篇)》MOOC课程 http://mooc.study.163.com/course/USTC-1000002006

一、《软件工程(C编码实践篇)》学习心得

学习软件工程可以少走弯路,可以了解一个专业的程序员的工作模式,并且理解学习四个代码质量层次:
1)代码风格规范,易于阅读;
2)封装接口简洁独立;
3)可重用代码;
4)可重入函数和线程安全
(PS:课程的实验环境是网易云课堂的实验楼环境,便于像我这样的git和Linux新手入手学习。不过代码量大时,建议先本地编辑好,再复制到剪切板或直接git到代码库,然后运行--git push。另外,代码规范是评分的重要标准,应严格遵守,可以参考这篇文章《代码规范》https://wenku.baidu.com/view/f76ee2c3b9d528ea81c77969.html)
C编码实践篇主要是从这几个代码质量层次方面入手,以一个menu小程序为切入点,从通用结构,到封装,信息隐藏,接口设计,到回调函数,可重用性和线程安全,抽象等,一步步了解程序模块化设计的过程,在整个实验的过程中,理解了软件工程是对如何构建和开发软件的科学指导性,软件工程设计思想是设计好的程序的坚实的基础。不应仅仅局限于功能的实现。软件 = 程序 + 软件工程 = 数据结构 + 算法

二、《C编码实践篇》六次课程实验报告

该课程一共有七次实验和一次考试,其中第六次实验是测试,不用写实验报告,因此在下面把六次课程实验要求及报告放在下面:

实验一:写一个hello world小程序

实验要求:写一个hello world小程序

  • 在实验楼Linux环境使用C语言编写,编译后执行输出”Hello,World!”;
  • 实验和实验报告务必在实验楼linux下完成,课程视频是在本地虚拟机上操作的,除了目录环境和作业提交方式不同外,基本的命令和编辑操作方式是一致的。

实验报告:https://www.shiyanlou.com/courses/reports/1270492

实验代码:

#include <stdio.h>int main(){printf("Hello World!\n");}

实验二:命令行菜单小程序V1.0

实验要求:

  • 实现一个命令行的菜单小程序,执行某个命令时调用一个特定的函数作- 为执行动作,实现的命令个数不少于8个;
  • 类似ftp的help目录或者bash的help目录;
  • 程序循环、接收用户的命令,如help、others等命令;
  • 可以广泛通用的命令行菜单子系统组件,可方便地定制而嵌入到其他系统;

实验报告:https://www.shiyanlou.com/courses/reports/1271950

实验代码:

#include<stdio.h>#include<stdlib.h>void show();int main(){    char cmd[128];    while(1)    {        scanf("%s",cmd);        if(strcmp(cmd,"hello")==0)        {            printf("Welcome to my menu ^.^");        }        if(strcmp(cmd,"help")==0)        {            printf("What can I do for you O.O ?");        }        if(strcmp(cmd,"show")==0)        {            show();        }        if(strcmp(cmd,"1")==0)        {            printf("First emoji TAT");        }        if(strcmp(cmd,"2")==0)        {            printf("Second emoji $.$");        }        if(strcmp(cmd,"3")==0)        {            printf("Third emoji >o<");        }        if(strcmp(cmd,"4")==0)        {            printf("Forth emoji ^o^");        }        if(strcmp(cmd,"exit")==0)        {            exit(0);        }    }}void show(){    printf("***************************\n");    printf("hello:Just welcome\n");    printf("help:Ask for help\n");    printf("show:Show all menu cmd\n");    printf("1:the 1st emoji\n");    printf("2:the 2nd emoji\n");    printf("3:the 3rd emoji\n");    printf("4:the 4th emoji\n");    printf("exit:exit the menu\n");    printf("****************************\n");}

实验三:内部模块化的命令行菜单小程序

实验要求:

  • 注意代码的业务逻辑和数据存储之间的分离,即将系统抽象为两个层级:菜单业务逻辑和菜单数据存储
  • 要求:1)遵守代码风格规范,参考借鉴代码设计规范的一些方法;2)代码的业务逻辑和数据存储使用不同的源文件实现,即应该有2个.c和一个.h作为接口文件。

实验报告:https://www.shiyanlou.com/courses/reports/1273109

实验代码:

引入链表,实现代码的业务逻辑和数据存储之间的分离
1)menu_list.h
#ifndef LINKLIST_H#define LINKLIST_Htypedef struct DataNode{    char* cmd;    char* desc;    void (*handler)();    struct DataNode* next;}tDataNode;tDataNode* FindCmd(tDataNode* head,char* cmd);void ShowAllCmd(tDataNode* head);#endif

2)  menu_list.c
#include<stdio.h>#include<stdlib.h>#include<string.h>#include "menu_list.h"tDataNode* FindCmd(tDataNode* head,char*cmd){    tDataNode* p = head;    while((p!=NULL)&&(strcmp(cmd,p->cmd)!=0))    {        p=p->next;    }    return p;}void ShowAllCmd(tDataNode* head){    tDataNode* p=head;    while(p)    {        printf("%s:%s\n",p->cmd,p->desc);        p=p->next;    }}

3)  menu.c
#include<stdio.h>#include<stdlib.h>#include "menu_list.h"void hello();void help();void show();void emoji_1();void emoji_2();void emoji_3();void emoji_4();void quit();tDataNode head[]={    {"hello","Welcome to my menu 2.0",hello,&head[1]},    {"help","What can I do for you O.O ?",help,&head[2]},    {"show","Show all menu cmd",show,&head[3]},    {"emoji_1","the first emoji cmd",emoji_1,&head[4]},    {"emoji_2","the second emoji cmd",emoji_2,&head[5]},    {"emoji_3","the third emoji cmd",emoji_3,&head[6]},    {"emoji_4","the forth emoji cmd",emoji_4,&head[7]},    {"quit","exit the menu cmd",quit,NULL}};int main(){    char cmd[128];    while(1)    {        scanf("%s",cmd);        tDataNode* p=FindCmd(head,cmd);        if(p)        {            p->handler();        }        else        {            printf("the menu doesn't include this cmd!\n");        }    }    return 0;}void hello(){    printf("Welcome to my menu 2.0 ^.^!\n");}void show(){    ShowAllCmd(head);}void help(){    printf("What can I do for you O.O ? you can press show to find what you want cmd!\n");}void emoji_1(){    printf("First emoji TAT !\n");}void emoji_2(){    printf("Second emoji $.$ !\n");}void emoji_3(){    printf("The third emoji >o< !\n");}void emoji_4(){    printf("The forth emoji ~^o^~ !\n");}void quit(){    exit(0);}

实验四:用可重用的链表模块来实现命令行菜单小程序

实验要求:

  • 用可重用的链表模块来实现命令行菜单小程序,执行某个命令时调用一个特定的函数作为执行动作;
  • 链表模块的接口设计要足够通用,命令行菜单小程序的功能保持不变;
  • 可以将通用的Linktable模块集成到我们的menu程序中;
  • 接口规范;

实验报告:https://www.shiyanlou.com/courses/reports/1275007 

实验代码:

1)linkable.h
#ifndef _LINK_TABLE_H_#define _LINK_TABLE_H_typedef struct LinkTableNode{    struct LinkTableNode *pNext;}tLinkTableNode;typedef struct LinkTable{    tLinkTableNode *pHead;    tLinkTableNode *pTail;    int SumOfNode;}tLinkTable;//Create a linktabletLinkTable * CreateLinkTable();//Delete linktableint DeleteLinkTable(tLinkTable * pLinkTable);//Add a linktable nodevoid AddLinkTableNode(tLinkTable * pLinkTable, tLinkTableNode * pNode);//Delete a linktable nodeint DeleteLinkTableNode(tLinkTable * pLinkTable, tLinkTableNode * pNode);//Get link table headtLinkTableNode * GetLinkTableHead(tLinkTable * pLinkTable);//Get next link table nodetLinkTableNode * GetNextLinkTableNode(tLinkTable * pLinkTable, tLinkTableNode * pNode);#endif
2)  linkable.c
#include <stdio.h>#include <stdlib.h>#include "linktable.h"tLinkTable * CreateLinkTable(){    tLinkTable * pLinkTable = ( tLinkTable *)malloc(sizeof (tLinkTable));    pLinkTable->pHead = NULL;    pLinkTable->pTail = NULL;    pLinkTable->SumOfNode = 0;    return pLinkTable;}int DeleteLinkTable ( tLinkTable * pLinkTable ){    tLinkTableNode * p;    while ( pLinkTable->pHead != pLinkTable->pTail )    {    p=pLinkTable->pHead;    pLinkTable->pHead = pLinkTable->pHead->pNext;    free (p);    }    free (pLinkTable->pHead);    free (pLinkTable);}void AddLinkTableNode ( tLinkTable * pLinkTable , tLinkTableNode * pNode){    if(pLinkTable->pHead == NULL)    {    pLinkTable->pHead = pNode;    pLinkTable->pTail = pNode;    pLinkTable->SumOfNode = 1;    }    else    {    pLinkTable->pTail->pNext = pNode;    pLinkTable->pTail = pNode;    pLinkTable->SumOfNode += 1;        }    }int DeleteLinkTableNode ( tLinkTable * pLinkTable , tLinkTableNode * pNode){    if ( pLinkTable == NULL || pNode == NULL)    {        return -1;    }    if(pLinkTable->pHead == pNode)    {        pLinkTable->pHead == pLinkTable->pHead->pNext;        pLinkTable->SumOfNode -= 1;        if(pLinkTable->SumOfNode == 0)        {            pLinkTable->pTail = NULL;        }           return 0;    }     tLinkTableNode *p = pLinkTable->pHead;    while(p != NULL)    {         if(p->pNext == pNode)        {            tLinkTableNode *q = p->pNext->pNext;            p->pNext = q;            free(q);            pLinkTable->SumOfNode -= 1;            if(pLinkTable->SumOfNode == 0)            {                pLinkTable->pTail = NULL;             }            return 0;        }        p = p->pNext;    }      return -1;}tLinkTableNode * GetLinkTableHead( tLinkTable * pLinkTable ){    if(pLinkTable == NULL)    {        return NULL;    }    return pLinkTable->pHead;}tLinkTableNode * GetNextLinkTableNode(tLinkTable *pLinkTable, tLinkTableNode *pNode){    if(pLinkTable == NULL || pNode == NULL)    {        return NULL;    }    tLinkTableNode *p = pLinkTable->pHead;    while(p != NULL)    {        if(p == pNode)        {            return p->pNext;        }        p = p->pNext;    }    return NULL;}
3)  menu.c
#include <stdio.h>#include <stdlib.h>#include <math.h>#include <string.h>#include <ctype.h>#include "linktable.h"#define CMD_MAX_LEN 10int Hello();int Help();int Show();int Emoji();int Ls();int Date();int Author();int Quit();typedef struct DataNode{    tLinkTableNode * pNext;    char * cmd;    char * desc;    int (*handler)();}tDataNode;tDataNode * Findcmd(tLinkTable * head, char * cmd){    tDataNode * pNode = (tDataNode *)GetLinkTableHead(head);    while(pNode != NULL)    {        if(strcmp(pNode->cmd, cmd) == 0)        {            return pNode;        }        else        {            pNode = (tDataNode *)GetNextLinkTableNode(head, (tLinkTableNode *)pNode);        }    }    return NULL;}int ShowAllcmd(tLinkTable * head){    tDataNode * pNode = (tDataNode *)GetLinkTableHead(head);    printf("*******************************\n");    while(pNode != NULL)    {        printf("************\n");        printf("%s - %s\n", pNode->cmd, pNode->desc);        pNode = (tDataNode *)GetNextLinkTableNode(head, (tLinkTableNode *)pNode);    }    return 0;}int InitMenuData(tLinkTable ** ppLinktable){    *ppLinktable = CreateLinkTable();    tDataNode * pNode = (tDataNode *)malloc(sizeof(tDataNode));    pNode = (tDataNode *)malloc(sizeof(tDataNode));    pNode->cmd = "hello";    pNode->desc = "Welcome to my menu 3.0";    pNode->handler = Hello;    AddLinkTableNode(*ppLinktable, (tLinkTableNode *)pNode);    pNode = (tDataNode *)malloc(sizeof(tDataNode));    pNode->cmd = "help";    pNode->desc = "What can I do for you O.O ?";    pNode->handler = Help;    AddLinkTableNode(*ppLinktable, (tLinkTableNode *)pNode);    pNode = (tDataNode *)malloc(sizeof(tDataNode));    pNode->cmd = "show";    pNode->desc = "Show all menu cmd";    pNode->handler = Show;    AddLinkTableNode(*ppLinktable, (tLinkTableNode *)pNode);    pNode = (tDataNode *)malloc(sizeof(tDataNode));    pNode->cmd = "emoji";    pNode->desc = "the emoji expression: TAT $.$ >o<  ~^o^~ ";    pNode->handler = Emoji;    AddLinkTableNode(*ppLinktable, (tLinkTableNode *)pNode);    pNode = (tDataNode *)malloc(sizeof(tDataNode));    pNode->cmd = "ls";    pNode->desc = "List all file in this directory";    pNode->handler = Ls;    AddLinkTableNode(*ppLinktable, (tLinkTableNode *)pNode);    pNode = (tDataNode *)malloc(sizeof(tDataNode));    pNode->cmd = "date";    pNode->desc = "Show the current date";    pNode->handler = Date;    AddLinkTableNode(*ppLinktable, (tLinkTableNode *)pNode);    pNode = (tDataNode *)malloc(sizeof(tDataNode));    pNode->cmd = "author";    pNode->desc = "the menu 3.0 by Sunday647.L";    pNode->handler = Author;    AddLinkTableNode(*ppLinktable, (tLinkTableNode *)pNode);    pNode = (tDataNode *)malloc(sizeof(tDataNode));    pNode->cmd = "quit";    pNode->desc = "Exit the menu cmd\n";    pNode->handler = Quit;    AddLinkTableNode(*ppLinktable, (tLinkTableNode *)pNode);    return 0;}tLinkTable * head = NULL;int main(){    InitMenuData(&head);    while(1)    {        char cmd[CMD_MAX_LEN];        printf("Welcome to my menu 3.0 ^.^ ,please input a cmd-->");        scanf("%s", cmd);        tDataNode *p = Findcmd(head, cmd);        if(p == NULL)        {            printf("the menu doesn't include this cmd!\n");            continue;        }        printf("%s - %s\n", p->cmd, p->desc);        if(p->handler != NULL)        {            p->handler();        }    }}int Hello(){    return 0;}int Help(){    return 0;}int Show(){    ShowAllcmd(head);    return 0;}int Emoji(){    return 0;}int Ls(){    system("ls");    return 0;}int Date(){    system("date");    return 0;}int Author(){    return 0;}int Quit(){    exit(0);}

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

实验要求:

  • 给lab5-1.tar.gz(在实验楼Linux虚拟机环境下~/se_files/目录下)找bug,quit命令无法运行的bug
  • 利用callback函数参数使Linktable的查询接口更加通用
  • 注意接口的信息隐藏

实验报告:https://www.shiyanlou.com/courses/reports/1276282

实验代码:

本次实验仍由menu.c,linktable.c,linktable.h三个文件组成,均在lab5-1.tar.gz里的源码基础上进行修改。

在实验四的基础上加了一个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 != NULL)    {            if(Conditon(pNode) == SUCCESS)        {            return pNode;            }        pNode = pNode->pNext;    }    return NULL;}
在linktable.h中只写类型声明, 真正的tLinkTable类型定义在linktable.c中,如下图所示
struct LinkTable{    tLinkTableNode *pHead;    tLinkTableNode *pTail;    intSumOfNode;    pthread_mutex_t mutex;};

实验七:将menu设计为可重用的子系统

实验要求:

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

实验报告:https://www.shiyanlou.com/courses/reports/1278241 

实验代码:

1)  在实验五的基础上修改menu,c,添加MenuConfig ()和ExecuteMenu()
int MenuConfig(char * cmd, char * desc, void (*handler)(int argc, char *argv[])){    tDataNode* pNode = NULL;    if (head == NULL)    {        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);}int ExecuteMenu(){    while(1)    {        int argc = 0;        char *argv[CMD_MAX_ARGV_LEN];        char cmd[CMD_MAX_LEN];        char *pcmd = NULL;        printf("Input a cmd number > ");        pcmd = fgets(cmd, CMD_MAX_LEN, stdin);        if (pcmd == NULL)        {            continue;        }        pcmd = strtok(pcmd, " ");        while (pcmd != NULL && argc < CMD_MAX_ARGV_LEN)        {            argv[argc] = pcmd;            argc++;            pcmd = strtok(NULL, " ");        }        if (argc == 1)        {            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);        }    }}
2)  添加menu.h
#ifndef _MENU_H#define _MENU_Hint MenuConfig(char * cmd, char * desc, void (*handler)(int argc, char *argv[]));int ExecuteMenu();#endif
3)  添加test.h
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <time.h>#include "menu.h"void quit(int argc, char *argv[]);void version(int argc, char *argv[]);void date(int argc, char *argv[]);void quit(int argc, char *argv[]){    exit(0);}void version(int argc, char *argv[]){       printf("Microsoft Windows 10.0.14393\n");}void date(int argc, char *argv[]){    time_t timep;    struct tm *p;    time(&timep);    p=gmtime(&timep);    printf("%d\\",1900+p->tm_year);    printf("%d\\",1+p->tm_mon);    printf("%d\n",p->tm_mday);}int main(int argc,char* argv[]){MenuConfig("version","XXX1.0(Menu program v7.0 inside)",NULL);MenuConfig("quit","Quit the program",quit);MenuConfig("date","Show the date",date);ExecuteMenu();return 0;}
4) Makefile 文件
## Makefile for Menu Program#CC_PTHREAD_FLAGS     = -lpthreadCC_FLAGS             = -cCC_OUTPUT_FLAGS      = -oCC_MATH              = -lmCC                   = gccRM                   = rmRM_FLAGS             = -fTARGET               = testOBJS                 = linktable.o menu.o test.oall:    $(OBJS)$(CC) $(CC_OUTPUT_FLAGS) $(TARGET) $(OBJS) $(CC_MATH).c.o:$(CC) $(CC_FLAGS) $<clean:$(RM) $(RM_FLAGS) $(OBJS) $(TARGET) *.bak

注意:makefile文件中all:以及clean:后不要使用空格,应用Tab键,否则会出错,*missing separator. Stop.

三、总结

总结:经过这个课程的学习,在老师的视频指导下,完成了对一个menu命令行小程序从通用结构到模块化、接口、信息隐藏、增量开发、抽象、代码重用等一系列的软件工程过程的实现,同时时刻强调代码规范,提高自己代码的可读性以及效率。通过此次的学习,打破了自己以往局限于功能实现的设计思想,能从系统大局出发,考虑可读性、可扩展性、可重用性等性能因素,并在具体代码中实践,从整体架构出发,提高了自己的代码质量以及效率。另外,通过这门课的学习,我对Linux一些基本的编程命令也有了一些了解。缺点是,自己还是没有扩大思维去做一个大型一点的实际项目,以后需要针对这方面多练习。毕竟实践才能出真知!
最后,感谢孟老师的教导,讲解很细很耐心,谢谢网易云课堂和实验楼,提供这样一个良好的平台和环境,谢谢~





  • 原创粉丝点击