代码中的软件工程
一、配置环境(以win10为例)
1. 安装C/C++编译器和调试器
- Step1:下载并安装MinGW(建议安装在默认的安装路径)
- Step2:配置环境变量(将MinGW的安装路径添加到Windows的path变量中)

然后打开cmd查看是否安装成功:

上图显示安装成功。
2. 安装vscode并进行相关配置
- Step1:安装vscode并下载下列插件:

- Step2:编写一个hello world程序看看是否运行成功

运行成功!
下边附上 launch.json文件和 tasks.json文件:
launch.json文件:
1 { 2 "version": "0.2.0", 3 "configurations": [ 4 { 5 "name": "编译并运行", 6 "type": "cppvsdbg", 7 "request": "launch", 8 "program": "C:\\Windows\\System32\\cmd.exe", //使用cmd.exe显示运行结果; 9 "args": [ 10 "/C", 11 "${workspaceFolder}\\bin\\${fileBasenameNoExtension}.exe", //填写.exe文件的存放路径,workspaceFolder为当前工作区路径; 12 "&", 13 "echo.", //换行; 14 "&", 15 "pause" //防止窗口闪退; 16 ], 17 "stopAtEntry": false, 18 "cwd": "${workspaceFolder}", 19 "environment": [], 20 "externalConsole": true, 21 "preLaunchTask": "C/C++: g++.exe build active file" 22 }, 23 { 24 "name": "使用GDB分步调试", 25 "type": "cppdbg", 26 "request": "launch", 27 "program": "${workspaceFolder}\\bin\\${fileBasenameNoExtension}.exe", //.exe文件的路径名; 28 "args": [], 29 "stopAtEntry": false, 30 "cwd": "${workspaceFolder}", 31 "environment": [], 32 "externalConsole": false, 33 "MIMode": "gdb", 34 "miDebuggerPath": "C:\\MinGW\\bin\\gdb.exe", //此处应填写本主机的gdb.exe路径名; 35 "setupCommands": [ 36 { 37 "description": "为 gdb 启用整齐打印", 38 "text": "-enable-pretty-printing", 39 "ignoreFailures": true 40 } 41 ], 42 "preLaunchTask": "C/C++: g++.exe build active file" 43 } 44 ] 45 }
tasks.json文件:
1 { 2 "tasks": [ 3 { 4 "type": "shell", 5 "label": "C/C++: g++.exe build active file", 6 "command": "C:\\MinGW\\bin\\g++.exe", //此处应当填写本主机的g++.exe文件路径; 7 "args": [ 8 "-g", 9 "${file}", 10 "-o", 11 "${workspaceFolder}\\bin\\${fileBasenameNoExtension}.exe" //.exe文件的存放路径; 12 ], 13 "options": { 14 "cwd": "${workspaceFolder}" 15 }, 16 "problemMatcher": [ 17 "$gcc" 18 ], 19 "group": { 20 "kind": "build", 21 "isDefault": true 22 } 23 } 24 ], 25 "version": "2.0.0" 26 }
2、代码的成长
根据老师的lab1到lab7.2版本的项目文件,可以发现一个项目从"Hello world"到一个完整的菜单功能小程序的成长历程。
lab1版本:

lab3.3版本:

lab7.2版本:

3. 模块化设计
模块化设计,简单地说就是程序的编写不是开始就逐条录入计算机语句和指令,而是首先用主程序、子程序、子过程等框架把软件的主要结构和流程描述出来,并定义和调试好各个框架之间的输入、输出链接关系。
模块化的目的是为了降低程序复杂度,使程序设计、调试和维护等操作简单化。改变某个子功能只需相应改变相应模块即可。
在软件工程中的模块化设计便是将一个将为庞大复杂的程序,拆分成多个部分,彼此之间相关性不大,但各部分本身内容很集中,即实现高内聚低耦合。耦合度是指软件模块之间的依赖程度,一般可以分为紧密耦合(Tightly Coupled)、松散耦合(Loosely Coupled)和无耦合(Uncoupled)。 一般在软件设计中我们追求松散耦合。
在lab1~lab3.2中,项目都没有设置头文件,由于前期的版本代码量较少,因此源代码只放置于一个文件无伤大雅,但是随着版本数的逐渐升高,代码量开始越来越大,因此再将全部代码都放置于一个文件中就显得过于杂乱。
例如,lab3.1:
1 /**************************************************************************************************/ 2 /* Copyright (C) mc2lab.com, SSE@USTC, 2014-2015 */ 3 /* */ 4 /* FILE NAME : menu.c */ 5 /* PRINCIPAL AUTHOR : Mengning */ 6 /* SUBSYSTEM NAME : menu */ 7 /* MODULE NAME : menu */ 8 /* LANGUAGE : C */ 9 /* TARGET ENVIRONMENT : ANY */ 10 /* DATE OF FIRST RELEASE : 2014/08/31 */ 11 /* DESCRIPTION : This is a menu program */ 12 /**************************************************************************************************/ 13 14 /* 15 * Revision log: 16 * 17 * Created by Mengning, 2014/08/31 18 * 19 */ 20 21 22 #include <stdio.h> 23 #include <stdlib.h> 24 25 int Help(); 26 int Quit(); 27 28 #define CMD_MAX_LEN 128 29 #define DESC_LEN 1024 30 #define CMD_NUM 10 31 32 typedef struct DataNode 33 { 34 char* cmd; 35 char* desc; 36 int (*handler)(); 37 struct DataNode *next; 38 } tDataNode; 39 40 static tDataNode head[] = 41 { 42 {"help", "this is help cmd!", Help,&head[1]}, 43 {"version", "menu program v1.0", NULL, &head[2]}, 44 {"quit", "Quit from menu", Quit, NULL} 45 }; 46 47 int main() 48 { 49 /* cmd line begins */ 50 while(1) 51 { 52 char cmd[CMD_MAX_LEN]; 53 printf("Input a cmd number > "); 54 scanf("%s", cmd); 55 tDataNode *p = head; 56 while(p != NULL) 57 { 58 if(strcmp(p->cmd, cmd) == 0) 59 { 60 printf("%s - %s\n", p->cmd, p->desc); 61 if(p->handler != NULL) 62 { 63 p->handler(); 64 } 65 break; 66 } 67 p = p->next; 68 } 69 if(p == NULL) 70 { 71 printf("This is a wrong cmd!\n "); 72 } 73 } 74 } 75 76 int Help() 77 { 78 printf("Menu List:\n"); 79 tDataNode *p = head; 80 while(p != NULL) 81 { 82 printf("%s - %s\n", p->cmd, p->desc); 83 p = p->next; 84 } 85 return 0; 86 } 87 88 int Quit() 89 { 90 exit(0); 91 }
lab3.2也是如此
1 /**************************************************************************************************/ 2 /* Copyright (C) mc2lab.com, SSE@USTC, 2014-2015 */ 3 /* */ 4 /* FILE NAME : menu.c */ 5 /* PRINCIPAL AUTHOR : Mengning */ 6 /* SUBSYSTEM NAME : menu */ 7 /* MODULE NAME : menu */ 8 /* LANGUAGE : C */ 9 /* TARGET ENVIRONMENT : ANY */ 10 /* DATE OF FIRST RELEASE : 2014/08/31 */ 11 /* DESCRIPTION : This is a menu program */ 12 /**************************************************************************************************/ 13 14 /* 15 * Revision log: 16 * 17 * Created by Mengning, 2014/08/31 18 * 19 */ 20 21 22 #include <stdio.h> 23 #include <stdlib.h> 24 25 int Help(); 26 27 #define CMD_MAX_LEN 128 28 #define DESC_LEN 1024 29 #define CMD_NUM 10 30 31 /* data struct and its operations */ 32 33 typedef struct DataNode 34 { 35 char* cmd; 36 char* desc; 37 int (*handler)(); 38 struct DataNode *next; 39 } tDataNode; 40 41 tDataNode* FindCmd(tDataNode * head, char * cmd) 42 { 43 if(head == NULL || cmd == NULL) 44 { 45 return NULL; 46 } 47 tDataNode *p = head; 48 while(p != NULL) 49 { 50 if(!strcmp(p->cmd, cmd)) 51 { 52 return p; 53 } 54 p = p->next; 55 } 56 return NULL; 57 } 58 59 int ShowAllCmd(tDataNode * head) 60 { 61 printf("Menu List:\n"); 62 tDataNode *p = head; 63 while(p != NULL) 64 { 65 printf("%s - %s\n", p->cmd, p->desc); 66 p = p->next; 67 } 68 return 0; 69 } 70 71 /* menu program */ 72 73 static tDataNode head[] = 74 { 75 {"help", "this is help cmd!", Help,&head[1]}, 76 {"version", "menu program v1.0", NULL, NULL} 77 }; 78 79 int main() 80 { 81 /* cmd line begins */ 82 while(1) 83 { 84 char cmd[CMD_MAX_LEN]; 85 printf("Input a cmd number > "); 86 scanf("%s", cmd); 87 tDataNode *p = FindCmd(head, cmd); 88 if( p == NULL) 89 { 90 printf("This is a wrong cmd!\n "); 91 continue; 92 } 93 printf("%s - %s\n", p->cmd, p->desc); 94 if(p->handler != NULL) 95 { 96 p->handler(); 97 } 98 99 } 100 } 101 102 int Help() 103 { 104 ShowAllCmd(head); 105 return 0; 106 }
真正的模块化设计思想从lab3.3开始。lab3.3将项目的不同功能分别放在不同的文件里,如菜单项目的功能部件放在linklist.c文件里,项目的主函数放置在menu.c文件里,对数据结构等的声明放在了头文件linklist.h里。使得整个项目的不同模块得以变得清晰。
lab3.3版本体现了KISS原则,即:一行代码只做一件事,一个块函数只做一件事,一个函数只做一件事,一个软件模块只做一件事。
lab3.3源码如下:
linklist.h文件:
1 /**************************************************************************************************/ 2 /* Copyright (C) mc2lab.com, SSE@USTC, 2014-2015 */ 3 /* */ 4 /* FILE NAME : linklist.h */ 5 /* PRINCIPAL AUTHOR : Mengning */ 6 /* SUBSYSTEM NAME : menu */ 7 /* MODULE NAME : linklist */ 8 /* LANGUAGE : C */ 9 /* TARGET ENVIRONMENT : ANY */ 10 /* DATE OF FIRST RELEASE : 2014/09/10 */ 11 /* DESCRIPTION : linklist for menu program */ 12 /**************************************************************************************************/ 13 14 /* 15 * Revision log: 16 * 17 * Created by Mengning, 2014/09/10 18 * 19 */ 20 21 22 /* data struct and its operations */ 23 24 typedef struct DataNode 25 { 26 char* cmd; 27 char* desc; 28 int (*handler)(); 29 struct DataNode *next; 30 } tDataNode; 31 32 /* find a cmd in the linklist and return the datanode pointer */ 33 tDataNode* FindCmd(tDataNode * head, char * cmd); 34 /* show all cmd in listlist */ 35 int ShowAllCmd(tDataNode * head);
linklist.c文件:
1 /**************************************************************************************************/ 2 /* Copyright (C) mc2lab.com, SSE@USTC, 2014-2015 */ 3 /* */ 4 /* FILE NAME : linklist.c */ 5 /* PRINCIPAL AUTHOR : Mengning */ 6 /* SUBSYSTEM NAME : menu */ 7 /* MODULE NAME : linklist */ 8 /* LANGUAGE : C */ 9 /* TARGET ENVIRONMENT : ANY */ 10 /* DATE OF FIRST RELEASE : 2014/09/10 */ 11 /* DESCRIPTION : linklist for menu program */ 12 /**************************************************************************************************/ 13 14 /* 15 * Revision log: 16 * 17 * Created by Mengning, 2014/09/10 18 * 19 */ 20 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include "linklist.h" 24 25 26 tDataNode* FindCmd(tDataNode * head, char * cmd) 27 { 28 if(head == NULL || cmd == NULL) 29 { 30 return NULL; 31 } 32 tDataNode *p = head; 33 while(p != NULL) 34 { 35 if(!strcmp(p->cmd, cmd)) 36 { 37 return p; 38 } 39 p = p->next; 40 } 41 return NULL; 42 } 43 44 int ShowAllCmd(tDataNode * head) 45 { 46 printf("Menu List:\n"); 47 tDataNode *p = head; 48 while(p != NULL) 49 { 50 printf("%s - %s\n", p->cmd, p->desc); 51 p = p->next; 52 } 53 return 0; 54 }
menu.c文件如下:
1 /**************************************************************************************************/ 2 /* Copyright (C) mc2lab.com, SSE@USTC, 2014-2015 */ 3 /* */ 4 /* FILE NAME : menu.c */ 5 /* PRINCIPAL AUTHOR : Mengning */ 6 /* SUBSYSTEM NAME : menu */ 7 /* MODULE NAME : menu */ 8 /* LANGUAGE : C */ 9 /* TARGET ENVIRONMENT : ANY */ 10 /* DATE OF FIRST RELEASE : 2014/08/31 */ 11 /* DESCRIPTION : This is a menu program */ 12 /**************************************************************************************************/ 13 14 /* 15 * Revision log: 16 * 17 * Created by Mengning, 2014/08/31 18 * 19 */ 20 21 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include "linklist.h" 25 26 int Help(); 27 28 #define CMD_MAX_LEN 128 29 #define DESC_LEN 1024 30 #define CMD_NUM 10 31 32 /* menu program */ 33 34 static tDataNode head[] = 35 { 36 {"help", "this is help cmd!", Help,&head[1]}, 37 {"version", "menu program v1.0", NULL, NULL} 38 }; 39 40 main() 41 { 42 /* cmd line begins */ 43 while(1) 44 { 45 char cmd[CMD_MAX_LEN]; 46 printf("Input a cmd number > "); 47 scanf("%s", cmd); 48 tDataNode *p = FindCmd(head, cmd); 49 if( p == NULL) 50 { 51 printf("This is a wrong cmd!\n "); 52 continue; 53 } 54 printf("%s - %s\n", p->cmd, p->desc); 55 if(p->handler != NULL) 56 { 57 p->handler(); 58 } 59 60 } 61 } 62 63 int Help() 64 { 65 ShowAllCmd(head); 66 return 0; 67 }
4. 可重用接口
接口是指对协定进行定义的引用类型。其他类型实现接口,以保证它们支持某些操作。接口指定必须由类提供的成员或实现它的其他接口。与类相似,接口可以包含方法、属性、索引器和事件作为成员。
接口使得模块与模块之间得以相互合作,且又能降低彼此的耦合性。
例如lab5.2就使用了许多接口:
1 typedef struct LinkTableNode 2 { 3 struct LinkTableNode * pNext; 4 }tLinkTableNode; 5 6 typedef struct LinkTable tLinkTable; 7 8 tLinkTable * CreateLinkTable(); 9 10 int DeleteLinkTable(tLinkTable *pLinkTable); 11 12 int AddLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode); 13 14 int DelLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode); 15 16 tLinkTableNode * SearchLinkTableNode(tLinkTable *pLinkTable, int Conditon(tLinkTableNode * pNode, void * args), void * args); 17 18 tLinkTableNode * GetLinkTableHead(tLinkTable *pLinkTable); 19 20 tLinkTableNode * GetNextLinkTableNode(tLinkTable *pLinkTable,tLinkTableNode * pNode);
5. 总结
对于今后的软件开发,我们要做到以下几点:
- 对于每个文件内部,我们应当保持一个良好的编码习惯:使用空格键将各个编码元素隔开;类名、变量名和函数名的命名遵守统一的命名规范;学会使用注释来帮助读者阅读源码等,从而使代码看上去尽可能简洁明了;
- 项目中的各个功能部件独立划分,遵从KISS原则,这样既有利于增加代码的可读性,也利于日常的维护和bug的修复;
- 各模块之间使用接口进行连接,并尽可能降低各模块之间的耦合程度,提升模块内部的内聚程度;
- 熟练掌握操作系统的基本知识,确保代码运行的线程安全。

浙公网安备 33010602011771号