简介:这份资源是南京航空航天大学2019—2020学年秋季学期数据结构课程设计的原创代码与配套报告,面向正在修读数据结构、需要完成课程设计或想通过实战加深理解的高校学生。内容覆盖排序、图论、哈夫曼树、家谱、邻接表等经典数据结构实验题目,代码均为个人独立编写,可作为课程作业参考与调试思路借鉴。压缩包共76个文件,约6.2MB,以36个cpp源码为主体,辅以31个txt测试数据与日志、6个exe可执行程序、1个dat数据文件及1份docx课程设计报告,源码、数据与报告相互对应,便于对照运行与验证。目前已有2788人学习下载,适合希望快速理清课程设计脉络、获取可运行代码与实验数据、并借助报告梳理设计思路的学习者参考使用。
1. 南京航空航天大学数据结构课程设计:一份代码加报告到底该做成什么样
如果你正在南航读大二大三,突然接到数据结构课程设计的任务,要求交一份能跑的代码加一份格式规范的报告,大概率第一反应是打开搜索引擎找“数据结构课程设计代码加报告”的现成模板。我当年也是这么干的,结果翻了一堆CSDN下载链接和GitHub仓库,发现大部分要么是只有代码没有报告,要么是报告里全是截图没有分析,要么代码跑起来一堆编译错误。后来自己从头做了一遍,又帮学弟学妹看了几份,才摸清楚这门课设真正卡人的地方在哪里。
南航的数据结构课程设计,通常安排在数据结构理论课结束之后,目的是让你把链表、栈、队列、树、图、排序、查找这些结构真正用C或C++写出来,而不是停留在选择题和填空题的层面。题目一般由老师给定若干个方向,比如通讯录管理系统、迷宫求解、哈夫曼编码、校园导航、成绩统计等,每个方向都要求你完成数据结构设计、核心算法实现、测试用例设计和一份符合学术规范的课程设计报告。代码要能编译运行,报告要有需求分析、概要设计、详细设计、调试分析、测试结果和心得体会。这两样东西合在一起,才是完整的交付物。
适合读这篇笔记的人很明确:你正在上这门课,或者即将开始做课设,手里有题目但不知道从哪下手,或者代码写完了但报告不知道怎么组织,又或者想提前了解一下这门课设的难度和工作量。下面我会按实际做课设的顺序,把选题、环境搭建、核心模块实现、报告撰写、调试排错这几个环节拆开讲,中间会给出可以直接参考的代码结构和报告框架,也会说清楚哪些地方容易翻车。
2. 选题与环境:从题目到可编译工程的第一步
2.1 南航课设常见题目类型与选题建议
南航数据结构课程设计的题目通常分为几大类,每类对应不同的数据结构侧重点。了解这些分类,能帮你在选题时判断工作量和技术难度,避免选了一个看起来简单但实际坑很深的题目。
第一类是线性表应用类,典型题目包括通讯录管理系统、学生成绩管理系统、图书信息管理系统。这类题目的核心是链表的增删改查,可能涉及顺序表和链表的对比,有的老师会要求同时实现两种存储结构并比较性能。工作量中等,难点在于菜单交互和文件读写,算法本身不复杂。
第二类是栈和队列应用类,比如迷宫求解、表达式求值、银行排队模拟。迷宫求解通常要求用栈实现深度优先搜索,表达式求值需要处理运算符优先级,银行排队模拟则涉及队列的入队出队和时间统计。这类题目的算法逻辑比线性表稍难,但代码量不大,适合对递归和栈操作比较熟的人。
第三类是树的应用类,最典型的是哈夫曼编码和二叉排序树。哈夫曼编码要求你从频率统计开始,构建哈夫曼树,生成编码表,再对文件进行压缩和解压。这个过程涉及树的构建、遍历、编码解码,工作量偏大,但报告写起来素材丰富,容易体现设计思路。
第四类是图的应用类,比如校园导航、最短路径规划、拓扑排序。校园导航通常要求用邻接矩阵或邻接表存储图,实现Dijkstra或Floyd算法求最短路径,有的还要求给出路径经过的具体地点。这类题目的算法理解成本较高,但代码结构清晰,测试用例也容易设计。
第五类是排序和查找综合类,比如成绩统计与排名系统,要求实现多种排序算法并比较时间性能。这类题目算法本身不难,但要求你做性能对比实验,报告里要有数据表格和结论分析,适合愿意花时间做实验记录的人。
选题时我一般建议优先考虑自己理论课学得最扎实的那一类,其次考虑报告素材是否充足。哈夫曼编码和校园导航虽然代码量大,但报告里可以画很多图、列很多表,写起来反而比通讯录管理系统更容易凑够篇幅。通讯录管理系统代码简单,但报告容易写得空洞,最后只能靠截图撑页面。
2.2 开发环境搭建与工程目录组织
南航课设通常要求用C或C++,IDE不限,但老师一般会推荐Dev-C++、CodeBlocks或Visual Studio。我建议用CodeBlocks或VS Code加MinGW,因为Dev-C++的调试功能太弱,出了问题只能靠printf,效率很低。如果你习惯用Visual Studio,注意创建项目时选择空项目,不要用预编译头,否则代码移植到其他环境会报错。
工程目录建议按功能模块组织,不要把所有代码塞进一个main.c里。一个清晰的目录结构不仅方便自己调试,也方便报告里写详细设计。我一般会这样组织:
project/ ├── include/ # 头文件目录 │ ├── linklist.h # 链表结构定义与函数声明 │ ├── huffman.h # 哈夫曼树相关声明 │ └── utils.h # 通用工具函数声明 ├── src/ # 源文件目录 │ ├── main.c # 主函数与菜单交互 │ ├── linklist.c # 链表操作实现 │ ├── huffman.c # 哈夫曼编码实现 │ └── utils.c # 工具函数实现 ├── data/ # 测试数据文件 │ ├── input.txt # 输入数据 │ └── output.txt # 输出结果 ├── Makefile # 编译脚本 └── README.md # 编译运行说明这个结构的好处是模块边界清晰,报告里的详细设计章节可以直接按文件来写。Makefile不是必须的,但写一个简单的编译脚本能省去每次手动输入gcc命令的麻烦:
CC = gcc CFLAGS = -Wall -g -Iinclude SRCS = src/main.c src/linklist.c src/huffman.c src/utils.c OBJS = $(SRCS:.c=.o) TARGET = dssystem $(TARGET): $(OBJS) $(CC) -o $@ $^ %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ clean: rm -f $(OBJS) $(TARGET)CFLAGS里的-Wall打开所有警告,-g保留调试信息,-Iinclude告诉编译器头文件在include目录下。这三个参数建议一直带着,很多潜在问题在编译阶段就能暴露出来。clean目标用于清理编译产物,重新编译前先执行一次,避免旧的目标文件干扰。
注意:如果你用的是Windows环境,Makefile里的
rm命令要改成del,或者直接用IDE的清理功能。另外路径分隔符在Windows下是反斜杠,但Makefile里统一用正斜杠也能正常工作。
环境搭好之后,先写一个最简单的main函数测试编译链路是否通畅,确认能生成可执行文件后再开始写业务代码。这一步花五分钟,能避免后面把编译错误和逻辑错误混在一起排查。
3. 核心数据结构实现:从链表到哈夫曼树的代码落地
3.1 链表模块的接口设计与边界处理
链表是课设里出现频率最高的结构,通讯录、成绩管理、图书管理都离不开它。很多人写链表时习惯把所有操作写在main函数里,结果代码超过五百行之后自己都找不到哪里改了指针。正确的做法是先定义清晰的接口,把结构体定义和函数声明放在头文件里,实现放在单独的源文件里。
以通讯录为例,头文件里先定义联系人结构体和链表节点:
// include/linklist.h #ifndef LINKLIST_H #define LINKLIST_H typedef struct { char name[32]; char phone[16]; char email[64]; } Contact; typedef struct Node { Contact data; struct Node *next; } Node; // 初始化空链表 Node* list_init(void); // 在链表尾部插入联系人,返回新的头指针 Node* list_append(Node *head, Contact c); // 按姓名查找,返回节点指针,未找到返回NULL Node* list_find(Node *head, const char *name); // 按姓名删除,返回新的头指针 Node* list_delete(Node *head, const char *name); // 打印全部联系人 void list_print(Node *head); // 释放整个链表 void list_free(Node *head); #endif接口设计的关键是明确每个函数的输入输出和所有权。list_append和list_delete返回新的头指针,因为删除头节点时头指针会变。list_find返回节点指针但不转移所有权,调用者不需要释放。list_free负责释放所有节点,调用后头指针失效。
实现文件里,插入操作要注意处理空链表的情况:
// src/linklist.c #include "linklist.h" #include <stdio.h> #include <stdlib.h> #include <string.h> Node* list_init(void) { return NULL; } Node* list_append(Node *head, Contact c) { Node *new_node = (Node*)malloc(sizeof(Node)); if (new_node == NULL) { fprintf(stderr, "内存分配失败\n"); return head; } new_node->data = c; new_node->next = NULL; if (head == NULL) { return new_node; } Node *p = head; while (p->next != NULL) { p = p->next; } p->next = new_node; return head; } Node* list_delete(Node *head, const char *name) { if (head == NULL) return NULL; if (strcmp(head->data.name, name) == 0) { Node *new_head = head->next; free(head); return new_head; } Node *p = head; while (p->next != NULL && strcmp(p->next->data.name, name) != 0) { p = p->next; } if (p->next != NULL) { Node *to_delete = p->next; p->next = to_delete->next; free(to_delete); } return head; }list_append里先分配新节点,如果malloc失败就打印错误并返回原头指针,避免程序崩溃。空链表时直接返回新节点作为头。非空时遍历到尾部再挂接。list_delete分两种情况:删除头节点和删除中间节点。删除头节点时直接释放并返回下一个节点作为新头;删除中间节点时先找到目标节点的前驱,修改指针后再释放。这里容易翻车的地方是忘记处理删除头节点的情况,导致头指针悬空。
list_find和list_print逻辑简单,遍历即可。list_free要保存下一个节点的指针再释放当前节点,否则释放后无法访问next:
void list_free(Node *head) { while (head != NULL) { Node *next = head->next; free(head); head = next; } }参数说明方面,Contact结构体里的name、phone、email长度是固定的,实际使用时要注意输入不要超过缓冲区大小。如果老师要求支持动态长度,可以把数组改成指针加长度字段,但那样内存管理会更复杂,课设阶段一般用固定长度就够了。
3.2 哈夫曼树构建与编码生成的完整流程
哈夫曼编码是树类题目里最典型的,也是报告素材最丰富的。整个流程分为四步:统计字符频率、构建哈夫曼树、生成编码表、用编码表压缩和解压。每一步都有对应的数据结构和算法,下面按顺序讲。
第一步统计频率。从输入文件读取字符,用一个大小为256的整型数组记录每个字符出现的次数。这里要注意文件可能包含换行符和空格,这些也要统计进去,否则解压时对不上。
// src/huffman.c #include "huffman.h" #include <stdio.h> #include <stdlib.h> #include <string.h> void count_frequency(const char *filename, int freq[256]) { memset(freq, 0, 256 * sizeof(int)); FILE *fp = fopen(filename, "rb"); if (fp == NULL) { fprintf(stderr, "无法打开文件: %s\n", filename); return; } int ch; while ((ch = fgetc(fp)) != EOF) { freq[ch]++; } fclose(fp); }用"rb"模式打开文件,避免Windows下换行符被自动转换。fgetc返回int而不是char,因为EOF是-1,用char接收会误判。统计完成后,freq数组里非零的位置就是需要编码的字符。
第二步构建哈夫曼树。定义树节点结构,包含字符、频率、左右孩子指针。用优先队列(最小堆)每次取出频率最小的两个节点合并,直到只剩一个节点,那就是根节点。
// include/huffman.h typedef struct HuffNode { unsigned char ch; // 字符,内部节点为0 int freq; // 频率 struct HuffNode *left; struct HuffNode *right; } HuffNode; HuffNode* build_huffman_tree(int freq[256]); void generate_codes(HuffNode *root, char *code, int depth, char *codes[256]); void free_huffman_tree(HuffNode *root);构建树的实现需要一个最小堆。课设阶段可以手写一个简单的堆,也可以用数组排序模拟。手写堆的代码量大约五十行,但能体现你对优先队列的理解,报告里也好写。下面是用数组模拟的简化版本:
static HuffNode* create_node(unsigned char ch, int freq) { HuffNode *node = (HuffNode*)malloc(sizeof(HuffNode)); node->ch = ch; node->freq = freq; node->left = NULL; node->right = NULL; return node; } HuffNode* build_huffman_tree(int freq[256]) { HuffNode *nodes[256]; int count = 0; for (int i = 0; i < 256; i++) { if (freq[i] > 0) { nodes[count++] = create_node((unsigned char)i, freq[i]); } } if (count == 0) return NULL; if (count == 1) return nodes[0]; // 每次找两个最小的节点合并 while (count > 1) { int min1 = 0, min2 = 1; if (nodes[min1]->freq > nodes[min2]->freq) { int tmp = min1; min1 = min2; min2 = tmp; } for (int i = 2; i < count; i++) { if (nodes[i]->freq < nodes[min1]->freq) { min2 = min1; min1 = i; } else if (nodes[i]->freq < nodes[min2]->freq) { min2 = i; } } HuffNode *parent = create_node(0, nodes[min1]->freq + nodes[min2]->freq); parent->left = nodes[min1]; parent->right = nodes[min2]; // 移除min1和min2,加入parent int new_count = 0; for (int i = 0; i < count; i++) { if (i != min1 && i != min2) { nodes[new_count++] = nodes[i]; } } nodes[new_count++] = parent; count = new_count; } return nodes[0]; }这段代码每次线性扫描找两个最小节点,时间复杂度是O(n²),对于256个字符来说完全够用。如果老师要求优化,可以改成堆实现,但课设阶段没必要过度设计。合并时左孩子放较小的节点,右孩子放较大的,这样生成的编码左分支为0右分支为1,规则统一。
第三步生成编码表。从根节点递归遍历,左走加'0',右走加'1',到达叶子节点时把当前编码字符串复制到codes数组对应位置。
void generate_codes(HuffNode *root, char *code, int depth, char *codes[256]) { if (root == NULL) return; if (root->left == NULL && root->right == NULL) { code[depth] = '\0'; codes[root->ch] = (char*)malloc(depth + 1); strcpy(codes[root->ch], code); return; } if (root->left != NULL) { code[depth] = '0'; generate_codes(root->left, code, depth + 1, codes); } if (root->right != NULL) { code[depth] = '1'; generate_codes(root->right, code, depth + 1, codes); } }code数组作为递归路径的缓冲区,depth记录当前深度。到达叶子时把路径字符串复制到codes数组,注意要malloc足够空间。如果只有一个字符,根节点既是叶子,编码为空字符串,这种情况要在压缩时特殊处理,否则解压会出问题。
第四步压缩和解压。压缩时遍历原文件每个字符,把对应的编码字符串写入输出文件。解压时从根节点开始,读到一个'0'走左,读到'1'走右,到达叶子就输出字符并回到根节点。
void compress_file(const char *input, const char *output, char *codes[256]) { FILE *fin = fopen(input, "rb"); FILE *fout = fopen(output, "wb"); if (fin == NULL || fout == NULL) return; int ch; while ((ch = fgetc(fin)) != EOF) { char *code = codes[ch]; fputs(code, fout); } fclose(fin); fclose(fout); }这个压缩版本按位写会更省空间,但课设阶段用字符'0'和'1'直接写文件也能接受,报告里可以提一句“实际应用中应按位存储,此处为简化实现”。解压时需要先重建哈夫曼树,可以从压缩文件头部读取频率表,或者约定压缩文件包含频率信息。简单做法是在压缩文件开头写入256个int的频率值,解压时先读频率再建树。
注意:哈夫曼树构建时如果两个节点频率相同,合并顺序会影响编码长度但不影响最优性。测试时可以用不同顺序验证压缩率是否一致,如果差异很大说明代码有问题。
4. 课程设计报告撰写:从需求分析到测试用例的完整框架
4.1 报告结构拆解与各章节写作要点
南航的课程设计报告一般有固定模板,但模板只给标题,具体内容怎么写全靠自己。我见过太多报告把详细设计写成代码注释的堆砌,或者把测试结果写成“运行成功”四个字。下面按章节拆解,说清楚每部分该写什么、写多深。
需求分析部分要写清楚程序要解决什么问题、输入是什么、输出是什么、有哪些功能模块。不要抄题目描述,要用自己的话重新组织。比如通讯录管理系统,可以写成“本程序用于管理联系人信息,支持添加、删除、查找、修改、显示全部联系人五项功能。输入为联系人姓名、电话、邮箱,输出为操作结果提示和联系人列表。”然后画一个功能模块图,用文字描述每个模块的职责。
概要设计部分要写数据结构选型和模块划分。链表、树、图这些结构为什么选它,不选别的,要给出理由。比如“通讯录使用单链表存储,因为联系人数量不确定,链表支持动态增删,不需要预先分配固定大小。”模块划分按功能分,每个模块对应一个或几个函数,说明模块之间的调用关系。
详细设计是报告的核心,要写每个模块的具体实现。但不要贴大段代码,而是用伪代码或流程图描述算法逻辑,再配合关键代码片段。比如哈夫曼树的构建,可以先写“每次从节点集合中选取频率最小的两个节点合并,直到只剩一个节点”,然后给出合并过程的伪代码,最后贴出核心的合并函数。这样既有逻辑描述又有实现细节,比纯贴代码或纯写文字都好。
调试分析部分要写实际调试过程中遇到的问题和解决方法。这部分最容易写得空洞,我建议按“问题现象→排查过程→原因分析→解决方案”的结构写。比如“程序在删除头节点后再次显示联系人时崩溃,排查发现删除后头指针未更新,导致访问了已释放的内存。解决方案是在删除函数中返回新的头指针,调用处用返回值更新头指针。”这样的记录既真实又有技术含量。
测试结果部分要设计测试用例,覆盖正常情况和边界情况。每个用例写清楚输入、预期输出、实际输出、是否通过。边界情况包括空链表删除、查找不存在的联系人、文件为空、只有一个字符的哈夫曼编码等。测试用例用表格呈现最清晰:
| 用例编号 | 测试功能 | 输入 | 预期输出 | 实际输出 | 结论 |
|---|---|---|---|---|---|
| TC01 | 添加联系人 | 姓名张三,电话123 | 提示添加成功,列表包含张三 | 同预期 | 通过 |
| TC02 | 删除头节点 | 删除列表中第一个联系人 | 列表更新,头指针指向第二个节点 | 同预期 | 通过 |
| TC03 | 查找不存在联系人 | 查找姓名李四 | 提示未找到 | 同预期 | 通过 |
| TC04 | 空链表删除 | 对空链表执行删除 | 提示链表为空 | 同预期 | 通过 |
心得体会部分不要写套话,写你真正学到的东西。比如“以前觉得链表很简单,这次自己实现才发现指针操作稍不注意就会内存泄漏,用valgrind检查后修正了三处未释放的内存。”这种具体的收获比“通过这次课设我加深了对数据结构的理解”有说服力得多。
4.2 代码注释规范与报告中的代码引用方式
课设代码的注释要适度,不是越多越好。函数头部写清楚功能、参数、返回值,关键逻辑处写清楚为什么这么做,而不是重复代码在做什么。比如list_delete函数里,删除头节点的分支要注释“头节点删除后需要更新头指针,否则调用者仍指向已释放内存”,而不是写“如果头节点匹配”。
报告里引用代码时,不要整段复制粘贴,而是截取关键片段,配合文字说明。比如讲哈夫曼树构建时,只贴合并两个最小节点的循环,然后说明“外层循环每次减少一个节点,直到只剩根节点”。完整代码放在附录里,正文只放核心逻辑。
代码风格要统一,缩进用四个空格或一个Tab,大括号位置一致,变量命名有意义。老师看报告时不一定逐行读代码,但代码风格差会留下不好的印象。函数名用动词开头,比如list_append、build_huffman_tree,变量名用名词,比如freq、codes。
提示:报告里的图表要有编号和标题,比如“图3-1 哈夫曼树构建流程”“表4-1 测试用例表”。引用时写“如图3-1所示”,不要写“如下图”。图表编号按章节来,第三章的图从3-1开始。
5. 避坑与排查:课设中那些让人熬夜的典型问题
5.1 内存泄漏与野指针的排查方法
链表和树的操作最容易出内存问题。常见现象是程序运行一段时间后崩溃,或者用valgrind检查发现大量未释放的内存。原因通常有两个:删除节点后没有把指针置空,或者遍历时释放了当前节点却继续访问它的next。
排查方法是用valgrind跑一遍程序,看泄漏报告里指向哪一行malloc。Linux和Mac下直接valgrind --leak-check=full ./dssystem,Windows下可以用Dr.Memory。如果报告显示某个结构体分配后未释放,就去检查对应的free调用是否在所有路径上都执行了。
解决野指针的习惯是:free之后立即把指针置为NULL,删除节点时先保存next再free。比如:
Node *to_delete = p->next; p->next = to_delete->next; free(to_delete); to_delete = NULL; // 避免后续误用5.2 文件读写中的编码与换行问题
哈夫曼编码和成绩统计都涉及文件读写。常见现象是压缩后再解压,文件内容与原文不一致,或者多出一些乱码。原因通常是文本模式和二进制模式混用,或者换行符被转换。
Windows下文本模式会把\n转换成\r\n,二进制模式不会。如果压缩时用文本模式读,解压时用二进制模式写,就会多出\r字符。统一用"rb"和"wb"能避免这个问题。另外统计频率时要把\r也统计进去,否则解压时对不上。
5.3 菜单交互中的输入缓冲区残留
菜单程序用scanf读整数后,回车符会留在缓冲区里,下一次读字符时直接读到回车,导致菜单跳过。现象是按了回车后程序连续执行两次循环。解决方法是在scanf后加getchar()吃掉回车,或者用fgets读整行再解析。
int choice; scanf("%d", &choice); getchar(); // 吃掉回车符如果输入的不是数字,scanf会失败并留下错误输入,导致死循环。更稳妥的做法是用fgets读一行,再用sscanf解析:
char line[64]; fgets(line, sizeof(line), stdin); sscanf(line, "%d", &choice);5.4 哈夫曼编码单字符文件的特殊情况
如果输入文件只有一个字符,哈夫曼树只有一个节点,编码为空字符串。压缩时写入空字符串,解压时无法确定输出什么。现象是压缩文件为空,解压后文件为空。解决方法是在构建树时判断节点数量,如果只有一个节点,手动给它分配编码“0”,并在解压时特殊处理。
5.5 报告查重与代码雷同的规避
南航课设报告会查重,代码也可能被比对。直接下载的模板代码和报告很容易被查出来。规避方法不是改改变量名,而是自己从头实现一遍,报告用自己的话写。参考别人的思路可以,但代码要自己敲,注释要自己写,测试用例要自己设计。如果参考了某个开源实现,在报告里注明参考来源,这比偷偷复制被查出来要好。
6. 让课设真正拿高分的几个进阶技巧
课设拿高分的关键不在于代码多复杂,而在于你有没有超出基本要求。基本要求是“能跑”,高分要求是“跑得好、说得清、有亮点”。下面几个技巧是我自己试过并且有效的。
第一个技巧是加性能对比实验。如果你做的是排序类题目,不要只实现一种排序,把冒泡、插入、快速、归并都实现一遍,用不同规模的数据跑时间,画成表格或折线图。报告里写“在10000个随机整数下,快速排序耗时2ms,冒泡排序耗时320ms,差距160倍”。这种数据比任何文字描述都有说服力。即使不是排序题,哈夫曼编码也可以对比不同文件的压缩率,通讯录可以对比顺序表和链表的插入效率。
第二个技巧是加异常处理。基本要求只考虑正常输入,高分要求考虑异常情况。比如通讯录输入超长姓名时截断并提示,文件不存在时给出友好错误而不是崩溃,内存分配失败时优雅退出。这些处理代码量不大,但报告里可以单独写一节“异常处理设计”,显得考虑周全。
第三个技巧是加简单的单元测试。不用引入测试框架,自己写一个test.c,里面调用各个模块的函数,用assert验证结果。比如:
#include <assert.h> #include "linklist.h" void test_list_append() { Node *head = list_init(); Contact c = {"张三", "123", "zhang@nuaa.edu.cn"}; head = list_append(head, c); assert(head != NULL); assert(strcmp(head->data.name, "张三") == 0); list_free(head); } void test_list_delete_head() { Node *head = list_init(); Contact c1 = {"张三", "123", ""}; Contact c2 = {"李四", "456", ""}; head = list_append(head, c1); head = list_append(head, c2); head = list_delete(head, "张三"); assert(strcmp(head->data.name, "李四") == 0); list_free(head); }报告里写“设计了8个单元测试用例,覆盖插入、删除、查找的边界情况,全部通过”。这比只写“运行成功”专业得多。
第四个技巧是报告排版。不要用默认的宋体小四加1.5倍行距就交上去。标题用黑体,正文用宋体,代码用Consolas或Courier New,图表居中并编号。页眉写课程名称和姓名学号,页脚写页码。这些细节花不了多少时间,但老师翻报告时第一印象会好很多。
最后一个习惯:提交前把代码在干净环境里重新编译运行一遍。我见过有人在自己电脑上跑得好好的,换到老师机器上因为路径问题或编译器版本问题跑不起来。把测试数据文件放在工程目录下,用相对路径读取,不要用绝对路径。Makefile里不要写死编译器路径,用CC = gcc让环境自己找。
希望帮到你。
本文还有配套的精品资源,点击获取