软件工程c编码随笔
写在前面的话
这是关于高级软件工程的实验课程的总结的文章,回顾整个实验课程,收获颇丰。
我是一个跨专业的学生,计算机相关的就只有c语言的基础,学这门课时,从一开始的不知所措,到现在的如此而已。从一个小小的命令行输出“helloworld”开始,到后来的menu命令行小程序1.0、2.0、2.5、2.8、3.0,再到最后的menu子系统,一步步的深入,逐渐加强模块化,逐渐接触到了对象封装、信息隐藏、接口实现、函数callback、带参命令、makefile编译工程等等,我想这就是我从这门课中学到的。
在这里我希望通过回顾整个实验过程,来加深我的理解,也希望能对像我之前一样需要帮助的读者有所助益。
《软件工程(C编码实践篇)》MOOC课程作业总结
首先,我们核心思想是:代码的可重用模块化,系统化。
其次,说明一下整体的思路和认识,我们实验是沿着一下步骤依次深入的,通过的不断地提高代码的模块化,增加其可重用性、通用性。
- 我们以一个hello world小程序来熟悉linux以及vim的操作。
- 使用简单的函数调用方法实现了命令行menu小程序,未实现模块化,仅仅简单的实现了menu的功能命令。
- 使用了结构体,将命令模块化,让各个命令能通用,进一步加强了模块化。
- 设计了链表结构,通过可重用的链表模块提供的接口来实现各个命令。
- 使用了callback函数调用机制来增强链表模块,加强信息隐藏,应该明白,我们声明的链表就好似面向对象中的类,是有封装性的,只能调用它的接口提供的函数方法和属性。
- 最后,我们将menu程序本身做成一个子系统,提供接口让上层系统调用,并设计成支持命令带参数。
下面是每一步的具体实现,在文章最后给出了相关的代码,以及我的原文链接和实验环境等。
实验一:写一个hello world小程序
思路和过程
顾名思义,这就是写一个命令行输出“hello world”的小程序,只需要c基础就可以做到。这里需要一些额外的知识,linux命令操作、vim编辑器以及git代码库提交的知识,当然还有代码规范等问题。需要的可以自行搜索。
这里列举一些常用的命令
linux:mkdir 创建目录、 cd 跳转路径、 vi 启动vim编辑、 mv 移动或改名文件、 rm 删除文件或目录、 cp 复制文件或目录
vi: i 进入编辑模式、 Esc键 退到普通模式、 ‘:’+“字母” 命令模式
git: git add 添加上传文件、 git commit 提交更改、 git push 上传- 首先在环境下用mkdir命令创建一个文件夹。
- 然后在文件夹里用VIM编辑器编辑一个hello.c的文件。
- 编辑完成后输入命令wq写文件并退出VIM,返回命令行环境。
- 再编译hello.c成一个hello可执行文件并执行。
- 这时就会显示“hello world!” 。
- 最后再使用git命令提交到自己的代码库里面。
实验心得
- 在编辑的c代码中,加入了一条return 0 语句用于主函数返回。
- 在试验开始之前,很难下手,由于本人不是软件专业的学生,对Linux系统完全不熟悉,花费了较多时间来属性相关的命令行操作。
总结
- 该试验的C代码编写可以说是非常简单的入门人员都会写的一个程序,其难点在于Linux系统下的命令行操作。
- 通过这个实验,我熟悉了Linux下的:添加文件夹,更改当前路径,获取文件列表,通过VIM编辑c代码,编译并执行等操作。
实验二:命令行菜单小程序V1.0
思路和过程
- 本次实验做了一个cmdmenu,命令行菜单小程序,主要实现了命令行下调用程序的基本操作,实现了几个简单的功能,有加、减、乘、除、平方、立方、help菜单、quit操作。
- 其中计算机对浮点数处理是近似化的,需要控制小数位数确保输出值正确。
关键代码
#include <stdio.h> #include <stdlib.h> void caidan() { printf("*****************This is cmdmenu********************\n"); printf(" + : This is add cmd\n"); printf(" - : This is sub cmd\n"); printf(" * : This is mul cmd\n"); printf(" / : This is div cmd\n"); printf(" pf : This is square cmd\n"); printf(" lf : This is cubic cmd\n"); printf(" help : This is help cmd\n"); printf(" quit : This is quit cmd\n"); printf("****************************************************\n"); } void jiafa() { double a,b; printf("a = "); scanf("%lf",&a); printf("b = "); scanf("%lf",&b); printf("a + b = %lf\n",(a+b)); } void jianfa() { double a,b; printf("a = "); scanf("%lf",&a); printf("b = "); scanf("%lf",&b); printf("a - b = %lf\n",(a-b)); } void chenfa() { double a,b; printf("a = "); scanf("%lf",&a); printf("b = "); scanf("%lf",&b); printf("a * b = %lf\n",(a*b)); } void chufa() { double a,b; printf("a = "); scanf("%lf",&a); printf("b = "); scanf("%lf",&b); printf("a / b = %lf\n",(a/b)); } void pingfang() { double a; printf("a = "); scanf("%lf",&a); printf("a^2= %lf\n",(a*a)); } void lifang() { double a; printf("a = "); scanf("%lf",&a); printf("a^3= %lf\n",(a*a*a)); } void bangzhu() { caidan(); } void tuichu() { exit(0); }
总结
- 本次实验熟悉了VIM编辑器,熟悉了Linux命令行的各种快捷操作,学会了VIM下的复制、剪切、删除、粘贴、跳行等操作。
实验三:内部模块化的命令行菜单小程序V2.0
实验思路
- 该试验主要是将代码规范化,将代码的业务逻辑和数据存储分离,这样就将系统抽象为两个层级:菜单业务逻辑和菜单数据结构方法,这样有利于对程序的扩充修改等操作。
- 在试验2的基础上,将代码修改为两个.c和一个.h文件,使得部分代码可以重用,使代码模块化。
关键代码
- 数据结构(linklist.h)
#include <stdio.h> #include <stdlib.h> #define cmd_len 128 #define cmd_desc_len 1024 typedef struct DataNode { char cmd[cmd_len] ; char desc[cmd_desc_len] ; int (* handler)(); struct DataNode * next ; } tDataNode ; tDataNode * FindCmd(tDataNode * head , char * cmd); int ShowAllCmd(tDataNode * head);
- 数据方法(linklist.c)
#include "linklist.h" tDataNode * FindCmd(tDataNode * head , char * cmd) { if(head == NULL || cmd == NULL) { return NULL; } tDataNode *p = head; while (p!=NULL) { if(strcmp(p->cmd,cmd) == 0) return p; p=p->next; } return NULL; } int ShowAllCmd(tDataNode * head) { printf("****************************************************\n"); tDataNode *p=head; while(p!=NULL) { printf("%s - %s\n",p->cmd,p->desc); p=p->next; } printf("****************************************************\n"); return 0; }
- 主程序(main函数部分)
int main() { char cmd[cmd_len]; ShowAllCmd(head); while(1) { printf("please input your command > "); scanf("%s", cmd); tDataNode *q=FindCmd(head,cmd); if(q == NULL) printf("ERROR COMMAND > %s\n",cmd); else { printf("%s\n",q->desc); if(q->handler != NULL) q->handler(); } } return 0; }
实验总结
- 本次实验难度不大,但需要考虑整个程序的模块化,多个文件模块间调用的问题、变量和指针的引用等地方容易出错。
实验四:用可重用的链表模块来实现命令行菜单小程序V2.5
实验过程
这次实验主要进行了两件事:
- 编写一个通用的可重用的规范化的链表模块及其接口,并编写一个链表测试示例文件。
- 将静态链表用通用的链表模块来实现,其小程序功能并不会改变,只是改变了链表的相关操作,由于通用链表提供了大量规范的接口,可以方便的调用这些接口函数,不必关心链表内部的具体实现。
关键代码
typedef struct LinkNode { struct LinkNode *Next; }tLinkNode; typedef struct LinkTable { tLinkNode *Head; tLinkNode *Tail; int num; }tLinkTable; tLinkTable* creatLinkTable(); int delLinkTable(tLinkTable* pLinkTable); int addLinkNode(tLinkTable* pLinkTable,tLinkNode* pNode); int delLinkNode(tLinkTable* pLinkTable,tLinkNode* pNode); tLinkNode* getHeadNode(tLinkTable* pLinkTable); tLinkNode* getTailNode(tLinkTable* pLinkTable); tLinkNode* getNextNodeinkTable* pLinkTable,tLinkNode* pNode);
实验总结
- 本次实验实现了接口的模块化规范化及其可重用性,并通过该接口实现了命令行菜单小程序,使程序的可扩充性提升很多,由于初始化较为繁琐,故减少了上个版本中的代数计算功能。
实验五:用callback增强链表模块来实现命令行菜单小程序V2.8
实验过程
- 给lab5-1.tar.gz(在实验楼Linux虚拟机环境下~/se_files/目录下)找bug,quit命令无法运行的bug,将lab5-1.tar.gz中的代码(即解压后lab5.1/目录下的源文件)直接放到Code/shiyanlou_cs122/lab5/目录下继续完成后面的实验内容
- 利用callback函数参数使Linktable的查询接口更加通用
- 接口的信息隐藏
关键代码
while(pNode != NULL)
改代码后bug消除,原因就是之前的代码是无法遍历到链表的尾节点,故而无法使用quit命令。
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; }c
- 链表的信息隐藏及其他代码详见代码库和截图。
实验总结
- 学习了callback函数的原理和使用方法,知道了该方法的优势。
- 学习了信息隐藏的方法。
- 学习了bug调试的方法。
实验七:将menu设计为可重用的子系统
实验过程
- 为menu子系统设计接口,并写用户范例代码来实现原来的功能
- 使用make和make clean来编译程序和清理自动生成的文件
- 使menu子系统支持带参数的复杂命令,使用getopt函数获取命令行参数并在用户范例代码中自定义一个带参数的复杂命令
关键代码
使用getopt函数获取命令行参数并在用户范例代码中自定义一个带参数的复杂命令。
int argtest(int argc, char **argv) { const char *optString = ":ab:c::"; opterr = 0; int ch; while ((ch = getopt(argc, argv, optString)) != -1) { switch (ch) { case 'a': printf("this -a option, no argument\n"); printf("In this cmd , you have 3 option can use:\n"); printf(" -a\n"); printf(" -b Argument\n"); printf(" -c OR -cArgument\n"); break; case 'b': printf("this -b option , argument is %s\n",optarg); break; case 'c': printf("this -c option , argument is %s\n",optarg); break; case '?': printf("this wrong option , wrong option is %c\n",(char)optopt); break; case ':': printf("this option need argument , option is %c\n",(char)optopt); break; default: printf("other option: %c\n",ch); break; } } optind = 0; return 0; }
编译方法Makefile见截图部分
实验总结
- 学习了Makefile编译工程的方法。
- 学会了带参数的命令行函数的编写。
- 学会了strtok和getopt函数的使用。
- 学会了编写子系统模块。
最后
到这里就完成了一个menu小程序到menu子系统的实现。
下面给出了我的实验报告链接及代码库,最后附上了课程及实验楼链接。
我的代码库 http://git.shiyanlou.com/cm/shiyanlou_cs122
我的实验报告,依次是实验1、2、3、4、5、7
https://www.shiyanlou.com/courses/reports/641767
https://www.shiyanlou.com/courses/reports/642499
https://www.shiyanlou.com/courses/reports/678720
https://www.shiyanlou.com/courses/reports/679782
https://www.shiyanlou.com/courses/reports/715700
https://www.shiyanlou.com/courses/reports/742813
实验楼地址https://www.shiyanlou.com/courses/122
《软件工程(C编码实践篇)》MOOC课程http://mooc.study.163.com/course/USTC-1000002006
- ExtJS之中文编码随笔
- 【编码随笔】boost::graph中adjacency_list用法与顶点信息绑定
- 【编码随笔】STL中系统常见自带函数对象小结(不断更新)
- 【编码随笔】使用atan2而不是atan
- 【编码随笔】Win32下定时器使用解释与示例
- 【编码随笔】数据类型、储存格式与fprintf
- JAVA随笔——关于编码
- 软件工程C编码实践课程总结
- 【编码随笔】生成带lib的dll
- 【编码随笔】双重指针作形参的数组问题
- jmeter随笔(9)--有两种编码风格,导致数据乱码
- 【编码随笔】重载运算符
- 【编码随笔】使用fprintf调试,结合使用fopen、fclose
- 关于编码、开发、分析、设计、项目管理的一些随笔
- 【编码随笔】了解C++11新特性
- 软件测试学习随笔(5) 编码的UI测试
- 随笔三十三:跳转指令及其编码
- 随笔-js汉字编码和解码,java汉字编码与解码
- 7-1通信系统中为什么要进行编码和解码?常见的编码方法有哪些?
- Mysql中文乱码问题完美解决方案【需要在my.cnf文件中设置client和mysqld的default-character-set=utf8,建数据库和表的时候也需要制定好编码】