简介这份数据结构课程设计航空订票系统文档面向计算机专业学生及需要完成课设或课程作业的学习者帮助解决航空订票业务模拟中的数据结构选型与算法实现问题。资源包共1个doc文件约1.18MB内容涵盖总体设计、概要设计、详细设计、调试分析、测试数据及截图、时间复杂度分析、问题思考、算法改进设想、课设总结体会与附录源代码等完整章节。文档以单链表、队列等结构组织航班、乘客与等候订票信息逐一说明录入、查询、订票、退票、修改航班及文件读写六大模块的算法思路并给出结构体定义与关键代码。已有1292人学习下载读者可据此获得可直接参考的课设方案、模块划分方法、线性表基本操作应用范例以及复杂度分析与改进方向适合作为课程设计报告撰写与代码实现的对照材料。1. 航空订票系统课设从链表到哈希表一个能过答辩的完整方案航空订票系统几乎是数据结构课程设计里出现频率最高的题目之一。每年期末都有大量同学在搜索引擎里翻「数据结构课程设计航空订票系统.doc」想找一份能直接参考的完整方案。但真正动手做过一遍的人都知道这个题目的坑不在于功能多复杂而在于数据结构选型直接决定了你后面是顺畅写完还是通宵改 bug。我带过几届课设也帮不少同学排查过代码。最常见的翻车场景是这样的用数组存航班和订单一开始增删改查都能跑等到要按航线、按时间、按余票排序查询时代码里全是嵌套循环改一个功能崩三个地方。问题出在选型阶段就没想清楚——航空订票系统的核心操作是高频查找和动态增删数组在这两件事上都不占优势。这篇文章面向正在做数据结构课设的本科生尤其是选了航空订票系统这个题目的同学。我会把整个系统的数据结构选型、核心模块实现、文件持久化、以及答辩时老师最爱追问的几个点讲清楚。代码用 C 语言写因为绝大多数高校的数据结构课设要求 C 或 C而且严蔚敏那本教材的代码风格就是 C。如果你用的是 Java 或 Python思路完全一样换语法就行。整篇内容按「选什么结构 → 怎么实现 → 哪里容易崩 → 怎么验证」推进每一步都给可编译的代码和参数说明。不搞伪代码糊弄也不贴一大段没有注释的代码让你自己猜。2. 数据结构选型航班、订单、乘客分别用什么存2.1 为什么航班表用哈希表而不是数组先明确系统的核心实体航班Flight、订单Order、乘客Passenger。这三个实体的操作特征完全不同不能用同一种结构一把梭。航班表的操作特征是什么查询频率远高于增删。用户打开系统第一件事就是查航班——按航班号查、按出发地和目的地查、按日期查。如果每次查询都遍历整个数组航班数量一多比如课设要求支持 100 条以上响应就会明显变慢。更关键的是课设答辩时老师很可能问你「查找的时间复杂度是多少」数组遍历是 O(n)而哈希表平均是 O(1)这个差距在答辩时是加分项。我一般用哈希表存航班哈希函数用航班号做 key。航班号通常是字母加数字的组合比如 CA1234、MU5678可以取数字部分对哈希表大小取模。冲突处理用链地址法每个桶挂一个链表。这样插入和查找的平均时间复杂度都是 O(1)最坏情况 O(n) 但实际不会碰到。#define HASH_SIZE 101 // 取质数减少冲突 typedef struct Flight { char flightNo[10]; // 航班号如 CA1234 char origin[20]; // 出发地 char dest[20]; // 目的地 char departTime[12]; // 起飞时间 int totalSeats; // 总座位数 int availableSeats; // 余票 float price; // 票价 struct Flight *next; // 哈希冲突链 } Flight; Flight *flightTable[HASH_SIZE]; // 哈希表每个元素是链表头指针 // 哈希函数取航班号中的数字部分 int hashFunc(const char *flightNo) { int num 0; for (int i 0; flightNo[i]; i) { if (flightNo[i] 0 flightNo[i] 9) { num num * 10 (flightNo[i] - 0); } } return num % HASH_SIZE; }哈希表大小取 101 是因为它是质数能让取模结果分布更均匀。如果你课设的航班数量比较少比如 20 条以内用 31 或 53 也行但别取 100 这种合数冲突会明显增多。hashFunc只提取数字部分因为航班号的字母前缀对区分度贡献不大而数字部分变化更丰富。插入航班的逻辑是算出哈希值找到对应桶头插法插入链表。查找时先算哈希值再遍历对应链表比对航班号。删除时先找到节点从链表摘除后 free。这三个操作都不复杂但要注意头插法会让同一个桶里的航班顺序和插入顺序相反如果你需要按插入顺序输出就得改成尾插或者在链表里额外维护顺序。2.2 订单和乘客用链表还是动态数组订单和乘客的操作特征跟航班不同。订单需要频繁插入每次订票产生一条也需要按订单号查询和按乘客查询。乘客数量相对稳定但每个乘客可能有多条订单。订单我用单链表存因为插入频繁且不需要随机访问。每条订单记录订单号、航班号、乘客身份证号、订票时间、座位号、状态已订/已退。链表的好处是插入 O(1)头插不需要预分配空间。缺点是按订单号查找是 O(n)但课设场景下订单量不会太大几百条以内链表完全够用。乘客信息我用一个动态数组存因为乘客需要按身份证号快速查找而且乘客数量可以预估上限。动态数组支持二分查找前提是按身份证号排序查找复杂度 O(log n)比链表的 O(n) 好。如果不想维护排序也可以用哈希表但乘客数量通常不多动态数组加二分就够了。typedef struct Order { char orderNo[16]; // 订单号 char flightNo[10]; // 关联的航班号 char idCard[20]; // 乘客身份证号 char bookTime[20]; // 订票时间 int seatNo; // 座位号 int status; // 0已订, 1已退 struct Order *next; } Order; typedef struct { char idCard[20]; char name[20]; char phone[15]; } Passenger; Passenger *passengers NULL; // 动态数组 int passengerCount 0; int passengerCapacity 0; // 动态数组扩容 void ensureCapacity() { if (passengerCount passengerCapacity) { passengerCapacity passengerCapacity 0 ? 16 : passengerCapacity * 2; passengers realloc(passengers, passengerCapacity * sizeof(Passenger)); } }扩容策略是每次翻倍从 16 开始。这样均摊插入成本是 O(1)。realloc之后要检查返回值是否为 NULL虽然课设场景下内存一般够用但养成检查习惯没坏处。乘客数组按身份证号排序后可以用bsearch做二分查找但每次插入新乘客后需要维护有序性插入排序的代价是 O(n)对于课设规模可以接受。2.3 座位分配用位图还是布尔数组座位分配是航空订票系统里容易被忽略但答辩时容易被问到的点。一架飞机有几十到几百个座位每个座位只有「已占」和「空闲」两种状态。最直观的做法是用一个布尔数组seat[i] 1表示第 i 个座位已占。但布尔数组在 C 语言里每个元素至少占 1 字节100 个座位就是 100 字节。用位图bitmap的话每个座位只占 1 bit100 个座位只要 13 字节。课设场景下内存不是瓶颈但位图是一个能体现你数据结构功底的选择。而且位图的操作——置位、清位、查询——都是位运算代码简洁且效率高。我一般用unsigned char数组做位图每个字节存 8 个座位的状态。#define MAX_SEATS 200 unsigned char seatMap[MAX_SEATS / 8 1]; // 置位标记第 seat 号座位已占 void setSeat(int seat) { seatMap[seat / 8] | (1 (seat % 8)); } // 清位释放第 seat 号座位 void clearSeat(int seat) { seatMap[seat / 8] ~(1 (seat % 8)); } // 查询返回 1 表示已占0 表示空闲 int isSeatTaken(int seat) { return (seatMap[seat / 8] (seat % 8)) 1; }位运算的三个操作要理解清楚|是置位 ~是清位加 1是查询。seat / 8定位到字节seat % 8定位到字节内的位。注意座位号从 0 开始还是从 1 开始要统一我一般从 0 开始因为位运算的下标从 0 算更方便。如果课设要求座位号从 1 开始显示在输出时加 1 就行内部存储统一用 0 基。选位图而不是布尔数组的另一个好处是答辩时你可以主动提「位图在操作系统和数据库里广泛用于空间管理」这是一个能展开讲的知识点。老师听到这种联系通常会认为你理解得比较深入。3. 核心功能实现订票、退票、查询的完整代码路径3.1 订票流程从输入航班号到写入订单文件订票是整个系统里逻辑链最长的操作涉及航班查询、余票判断、座位分配、订单生成、文件写入五个步骤。任何一个环节出问题都会导致订票失败或数据不一致。先看完整代码再逐步拆解int bookTicket(const char *flightNo, const char *idCard, const char *name) { // 第一步查找航班 int h hashFunc(flightNo); Flight *f flightTable[h]; while (f strcmp(f-flightNo, flightNo) ! 0) { f f-next; } if (!f) { printf(航班 %s 不存在\n, flightNo); return -1; } // 第二步检查余票 if (f-availableSeats 0) { printf(航班 %s 已满\n, flightNo); return -2; } // 第三步分配座位找第一个空闲座位 int seat -1; for (int i 0; i f-totalSeats; i) { if (!isSeatTaken(i)) { seat i; setSeat(i); break; } } if (seat -1) { printf(座位分配失败\n); return -3; } // 第四步生成订单 Order *o (Order *)malloc(sizeof(Order)); sprintf(o-orderNo, ORD%ld, time(NULL)); strcpy(o-flightNo, flightNo); strcpy(o-idCard, idCard); strcpy(o-bookTime, getCurrentTime()); o-seatNo seat; o-status 0; o-next orderList; orderList o; // 第五步更新余票并写文件 f-availableSeats--; saveOrdersToFile(); saveFlightsToFile(); printf(订票成功座位号%d\n, seat 1); return 0; }第一步的哈希查找前面已经讲过注意while循环的条件是f strcmp(...) ! 0两个条件缺一不可。如果只写strcmp不判空航班号不存在时会对 NULL 指针解引用直接段错误。第二步检查余票availableSeats在每次订票后减一退票后加一。这个字段和座位位图是两套独立的状态必须同步更新。我见过有同学只更新了位图忘了更新余票结果座位全占满了但系统还显示有余票订票时分配座位失败。第三步分配座位用的是线性扫描从 0 号座位开始找第一个空闲的。时间复杂度 O(n)n 是座位数。对于课设规模通常不超过 200 个座位这个效率完全够用。如果想优化可以维护一个「下一个空闲座位」的指针但没必要反而增加代码复杂度。第四步生成订单号用的是time(NULL)秒级时间戳。如果同一秒内有多条订单订单号会重复。课设场景下不太可能同一秒订两次但如果你要严谨可以加一个自增计数器拼在时间戳后面。第五步写文件是持久化的关键。每次订票后把订单链表和航班哈希表全部写回文件保证程序退出后数据不丢。全量写入在数据量小时没问题数据量大时可以考虑只追加新订单但课设不用做到那个程度。3.2 退票逻辑状态标记与座位释放的顺序退票比订票简单但有一个顺序问题容易搞错先释放座位还是先改订单状态正确的顺序是先找到订单把订单状态改为「已退」然后释放座位位图最后增加航班余票。如果先释放座位再改订单状态中间程序崩溃的话座位被释放了但订单还是「已订」状态数据不一致。int cancelTicket(const char *orderNo) { // 查找订单 Order *o orderList; while (o strcmp(o-orderNo, orderNo) ! 0) { o o-next; } if (!o) { printf(订单 %s 不存在\n, orderNo); return -1; } if (o-status 1) { printf(订单 %s 已退票请勿重复操作\n, orderNo); return -2; } // 先改状态 o-status 1; // 再释放座位 clearSeat(o-seatNo); // 最后更新航班余票 int h hashFunc(o-flightNo); Flight *f flightTable[h]; while (f strcmp(f-flightNo, o-flightNo) ! 0) { f f-next; } if (f) { f-availableSeats; } saveOrdersToFile(); saveFlightsToFile(); printf(退票成功\n); return 0; }注意clearSeat的参数是o-seatNo这个座位号是订票时分配的 0 基索引。如果你在订票时存的是 1 基座位号这里就要减 1否则会清错位。这种下标不一致的 bug 很隐蔽因为程序不会崩溃只是座位状态错了等到下次订票时才会发现。重复退票的判断也不能少。如果用户对同一个订单退两次第一次已经释放了座位并增加了余票第二次再执行会导致余票多加一次、座位被重复释放虽然清位操作是幂等的但余票会错。所以status 1的判断必须放在最前面。3.3 多条件查询按航线、时间、余票组合筛选查询功能是课设里代码量最大但逻辑最简单的部分。用户可能按出发地查、按目的地查、按日期查或者组合条件查。实现方式就是遍历航班哈希表的所有桶对每个航班检查是否满足所有条件。void searchFlights(const char *origin, const char *dest, const char *date) { int found 0; for (int i 0; i HASH_SIZE; i) { Flight *f flightTable[i]; while (f) { int match 1; if (origin strlen(origin) 0 strcmp(f-origin, origin) ! 0) { match 0; } if (dest strlen(dest) 0 strcmp(f-dest, dest) ! 0) { match 0; } if (date strlen(date) 0 strncmp(f-departTime, date, 10) ! 0) { match 0; } if (match) { printf(%-8s %-10s - %-10s %s 余票:%d 票价:%.0f\n, f-flightNo, f-origin, f-dest, f-departTime, f-availableSeats, f-price); found; } f f-next; } } if (found 0) { printf(没有找到符合条件的航班\n); } }条件判断的逻辑是空字符串或 NULL 表示该条件不参与筛选。strncmp(f-departTime, date, 10)只比较前 10 个字符因为日期格式是YYYY-MM-DD前 10 位就是日期部分后面的时间部分不参与比较。这样用户输入2024-01-15就能匹配到当天所有航班。遍历哈希表的所有桶是 O(HASH_SIZE n)其中 n 是航班总数。虽然不如直接按 key 查快但多条件查询本来就没法用哈希直接定位全表扫描是合理的。如果课设要求高性能多条件查询可以额外维护按航线或按日期的索引但那是进阶内容基础版本全表扫描足够。输出格式用了%-8s这样的左对齐占位符让输出整齐。%.0f表示不显示小数位票价一般不需要小数。这些格式化细节虽然小但答辩演示时整齐的输出会给老师好印象。4. 文件持久化与数据一致性别让程序一关数据就没了4.1 航班和订单的文件格式设计课设程序通常要求数据能保存到文件下次打开程序时能读回来。文件格式的设计直接影响读写代码的复杂度。常见的选择有两种文本格式和二进制格式。文本格式可读性好调试时能直接用记事本打开看但解析麻烦要处理分隔符和类型转换。二进制格式读写快直接fwrite整个结构体但可读性差而且结构体里有指针的话不能直接写指针地址下次运行就无效了。我一般用文本格式每行一条记录字段之间用|分隔。|在正常数据里不会出现比逗号安全地名里可能有逗号。航班文件的格式如下CA1234|北京|上海|2024-01-15 08:00|180|45|1200 MU5678|广州|成都|2024-01-15 10:30|200|12|980每行 7 个字段航班号、出发地、目的地、起飞时间、总座位、余票、票价。读写代码用fgets读一行然后用strtok按|拆分。void saveFlightsToFile() { FILE *fp fopen(flights.txt, w); if (!fp) return; for (int i 0; i HASH_SIZE; i) { Flight *f flightTable[i]; while (f) { fprintf(fp, %s|%s|%s|%s|%d|%d|%.0f\n, f-flightNo, f-origin, f-dest, f-departTime, f-totalSeats, f-availableSeats, f-price); f f-next; } } fclose(fp); } void loadFlightsFromFile() { FILE *fp fopen(flights.txt, r); if (!fp) return; char line[256]; while (fgets(line, sizeof(line), fp)) { Flight *f (Flight *)malloc(sizeof(Flight)); char *token strtok(line, |); if (!token) { free(f); continue; } strcpy(f-flightNo, token); token strtok(NULL, |); if (!token) { free(f); continue; } strcpy(f-origin, token); token strtok(NULL, |); if (!token) { free(f); continue; } strcpy(f-dest, token); token strtok(NULL, |); if (!token) { free(f); continue; } strcpy(f-departTime, token); token strtok(NULL, |); f-totalSeats token ? atoi(token) : 0; token strtok(NULL, |); f-availableSeats token ? atoi(token) : 0; token strtok(NULL, |); f-price token ? atof(token) : 0; f-next NULL; // 插入哈希表 int h hashFunc(f-flightNo); f-next flightTable[h]; flightTable[h] f; } fclose(fp); }strtok的用法要注意第一次调用传入字符串指针后续调用传 NULL。每次返回一个字段返回 NULL 表示没有更多字段了。每个字段都要判空否则文件格式不对时会段错误。atoi和atof分别把字符串转成整数和浮点数。加载时用头插法插入哈希表所以加载后的链表顺序和文件里的顺序相反。这不影响功能但如果你的输出依赖顺序就要改成尾插。另外loadFlightsFromFile在程序启动时调用一次调用前要确保flightTable已经初始化为全 NULL。4.2 写回时机每次操作后写还是退出时统一写写回时机的选择是一个权衡。每次操作后立即写文件数据最安全程序崩溃也不丢数据但频繁的磁盘 I/O 会拖慢程序。退出时统一写性能好但如果程序异常退出比如段错误所有未保存的数据都丢了。课设场景下我建议每次操作后写。原因有两个一是课设的数据量小写文件的耗时可以忽略二是答辩演示时如果程序崩了至少之前的数据还在不会出现「演示到一半数据全没了」的尴尬。但每次操作后写要注意一个问题如果一次操作涉及多个文件的修改比如订票要同时改航班文件和订单文件两个save调用之间如果程序崩溃会导致两个文件不一致。严格的解决方案是用临时文件加原子重命名但课设不用做到这个程度。我一般把两个save放在相邻位置中间不插入其他逻辑降低不一致的概率。// 订票成功后 f-availableSeats--; saveOrdersToFile(); // 先写订单 saveFlightsToFile(); // 再写航班顺序上先写订单再写航班。如果只写了一个文件就崩溃订单文件里有新订单但航班文件里余票没减下次启动时会出现「有订单但余票没扣」的情况。反过来先写航班的话余票减了但订单没写会出现「余票少了但没有对应订单」。两种不一致都存在但后者更隐蔽余票少了但找不到原因前者至少能通过订单追溯到。所以先写订单。4.3 内存泄漏排查valgrind 在课设里的实际用法课设代码里malloc了但忘记free是常见问题。程序运行时间短内存泄漏不会导致崩溃但答辩时如果老师问「你的内存管理做得怎么样」答不上来就尴尬了。更实际的是有些学校的课设评分会检查内存泄漏。排查内存泄漏最直接的工具是 valgrind。在 Linux 下编译时加-g保留调试信息然后用 valgrind 运行gcc -g -o airline airline.c valgrind --leak-checkfull ./airlinevalgrind 会输出所有未释放的内存块及其分配位置。典型的输出会指出哪一行malloc的内存在程序退出时还没free。对于课设程序主要检查三处航班链表节点、订单链表节点、乘客动态数组。修复方法是在程序退出前遍历所有链表和数组逐个free。哈希表的每个桶是一条链表要遍历每条链表释放所有节点。订单链表同理。乘客数组直接free(passengers)就行。void cleanup() { // 释放航班哈希表 for (int i 0; i HASH_SIZE; i) { Flight *f flightTable[i]; while (f) { Flight *tmp f; f f-next; free(tmp); } flightTable[i] NULL; } // 释放订单链表 Order *o orderList; while (o) { Order *tmp o; o o-next; free(tmp); } orderList NULL; // 释放乘客数组 free(passengers); passengers NULL; }cleanup在main函数返回前调用。注意释放链表时要先用临时指针保存下一个节点再free当前节点否则free之后访问next是未定义行为。这个顺序错误很常见而且 valgrind 不一定能检测出来因为访问已释放内存不一定崩溃但它是实实在在的 bug。5. 避坑与排查课设答辩前最容易翻车的 5 个问题5.1 哈希表大小取合数导致查询变慢现象程序功能都正常但航班数量增加到几十条后查询明显变慢答辩演示时老师能感觉到卡顿。原因哈希表大小取了 100 这样的合数。合数会导致哈希值分布不均匀大量航班集中在少数几个桶里链表变长查找退化接近 O(n)。解决哈希表大小取质数101、211、503 都可以。如果已经写了合数改成最近的质数即可代码不用大改。改完后可以用一个简单的测试验证分布插入 50 条航班打印每个桶的链表长度看是否均匀。5.2 座位号 0 基和 1 基混用导致退票清错位现象订票正常退票也显示成功但退票后再订同一航班发现座位号跳过了被退的那个或者两个订单分到了同一个座位。原因订票时存储的座位号是 0 基内部位图索引但退票时传入clearSeat的是 1 基用户看到的座位号导致清错了位。或者反过来订票存 1 基退票传 0 基。解决统一内部存储用 0 基只在用户界面显示时加 1。订票函数里o-seatNo seat0 基退票函数里clearSeat(o-seatNo)0 基输出时printf(座位号%d, o-seatNo 1)。所有涉及座位号的地方都检查一遍确保转换只发生在显示层。5.3 文件读写时 strtok 修改原字符串导致后续判断出错现象从文件加载航班后某些字段的值不对比如出发地变成了空字符串或者目的地和出发地互换。原因strtok会修改原字符串把分隔符替换成\0。如果在调用strtok之后还想用原始行做其他判断比如判断行是否为空、是否以 # 开头原始行已经被破坏了。解决如果需要在拆分后保留原始行先把行复制一份到临时缓冲区对副本调用strtok。或者调整逻辑先做行级判断再做字段拆分。课设里最常见的场景是跳过注释行或空行这个判断要放在strtok之前。5.4 动态数组 realloc 失败未处理导致段错误现象程序在添加大量乘客后突然崩溃崩溃位置在passengers[passengerCount] newPassenger这一行。原因realloc返回 NULL 时没有检查直接把 NULL 赋给了passengers后续访问passengers[i]就是对 NULL 指针解引用。解决realloc之后必须检查返回值。如果返回 NULL说明内存分配失败应该打印错误信息并返回而不是继续使用。注意不能直接passengers realloc(passengers, ...)因为如果realloc失败原来的passengers指针就丢了造成内存泄漏。正确做法是用临时指针接收返回值检查后再赋值。Passenger *tmp realloc(passengers, newCapacity * sizeof(Passenger)); if (!tmp) { printf(内存分配失败\n); return; } passengers tmp; passengerCapacity newCapacity;5.5 答辩时被问「时间复杂度」答不上来现象功能演示都过了老师问「你这个查找的时间复杂度是多少」支支吾吾说不清楚。原因写代码时只关注功能实现没有分析过每个操作的时间复杂度。解决提前把核心操作的时间复杂度整理清楚。哈希表查找平均 O(1)最坏 O(n)链表插入 O(1)头插查找 O(n)动态数组随机访问 O(1)插入 O(n)需要搬移元素位图置位/清位/查询都是 O(1)。二分查找 O(log n)。把这些记住答辩时能脱口而出。更进一步可以准备一句「如果数据量继续增大我会考虑用平衡树把最坏情况从 O(n) 降到 O(log n)」显示你有进阶思考。6. 从能跑到能拿高分三个让课设脱颖而出的进阶技巧6.1 用命令行参数支持批量导入航班基础版本的航班数据要么硬编码在代码里要么通过菜单逐条输入。答辩时老师如果想测试大量数据逐条输入太慢。加一个命令行参数支持从文件批量导入演示效果会好很多。int main(int argc, char *argv[]) { initTables(); loadFlightsFromFile(); // 先加载已有数据 if (argc 1 strcmp(argv[1], -i) 0 argc 2) { importFlights(argv[2]); // 从指定文件批量导入 } loadOrdersFromFile(); showMenu(); cleanup(); return 0; }importFlights函数读取一个格式和flights.txt相同的文件逐行解析后插入哈希表。这样演示时可以先准备一个包含 100 条航班的文件用./airline -i flights_big.txt一键导入比手动输入快得多。老师看到这个功能会认为你考虑了实际使用场景。6.2 用简单的单元测试验证核心逻辑课设不要求写测试但如果你能展示几个针对核心函数的测试用例答辩时会显得很专业。不需要引入测试框架写一个test()函数在main开头调用测试通过后进入正常菜单。void test() { // 测试哈希函数分布 int buckets[10] {0}; for (int i 0; i 100; i) { char no[10]; sprintf(no, CA%04d, i); buckets[hashFunc(no) % 10]; } printf(哈希分布测试); for (int i 0; i 10; i) printf(%d , buckets[i]); printf(\n); // 测试位图 setSeat(5); assert(isSeatTaken(5) 1); clearSeat(5); assert(isSeatTaken(5) 0); printf(位图测试通过\n); }assert在assert.h里测试失败会直接终止程序并打印失败位置。哈希分布测试打印每个区间的航班数量如果分布明显不均比如某个区间有 50 个而其他区间只有几个说明哈希函数或表大小需要调整。位图测试验证置位和清位的正确性这种基础测试能覆盖大部分低级错误。6.3 用 Makefile 管理编译别让老师看到一长串 gcc 命令课设代码通常分多个文件flight.c、order.c、main.c、file.c。如果每次编译都手敲gcc -o airline flight.c order.c main.c file.c容易漏文件也容易打错。写一个简单的 Makefilemake一键编译make clean清理。CC gcc CFLAGS -g -Wall -stdc99 OBJS flight.o order.o file.o main.o TARGET airline $(TARGET): $(OBJS) $(CC) $(CFLAGS) -o $(TARGET) $(OBJS) %.o: %.c $(CC) $(CFLAGS) -c $ -o $ clean: rm -f $(OBJS) $(TARGET)-Wall开启所有警告能帮你发现很多潜在问题比如未使用的变量、类型不匹配、忘记返回值。-stdc99指定 C 标准避免不同编译器默认标准不一致导致的兼容问题。make clean清理编译产物重新编译时不会用到旧的.o文件。我自己的习惯是每次改完代码先make clean make确保全量编译没有警告。课设代码量不大全量编译也就一两秒但能避免增量编译时旧目标文件导致的行为不一致。这个习惯帮我省了很多「明明改了代码但运行结果没变」的排查时间。最后说一个我踩过的坑有次答辩前把代码从 Windows 拷到 Linux 编译文件路径用了反斜杠\Linux 下直接找不到文件。后来统一用正斜杠/两个平台都能跑。如果你也在两个平台之间切换文件路径和换行符\r\nvs\n是两个必须检查的点。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?