作为 C 语言新手,想要通过实战项目巩固知识点,航空售票管理系统是非常合适的选择 —— 它覆盖了结构体、链表、文件操作、分支循环、函数封装等核心知识点,逻辑清晰且贴近实际应用。本文会从项目设计思路、核心功能实现、关键知识点解析三个维度,手把手拆解这个航空售票系统的开发过程(源码放在结尾)。
一、项目整体设计思路
1. 项目定位
本系统实现管理员和普通用户 / 会员两类角色的核心操作:
- 管理员:航班增删改查、用户管理;
- 会员:充值、购票、退票、订单查询、密码修改;
- 普通游客:查询航班、购票(无会员折扣 / 订单记录)。
2. 核心数据结构设计
C 语言的精髓是 “数据结构 + 算法”,本项目通过结构体 + 链表实现动态数据管理(相比数组,链表更适合频繁增删的场景):
(1)订单结构体(Order)
记录用户购票信息,通过链表挂载到用户结构体中:
typedef struct Order {
int flightId; // 关联航班ID
int quantity; // 购票数量
float price; // 单价
int isVip; // 是否会员价购票
struct Order* next;// 链表节点
} Order;
(2)航班结构体(Flight)
存储航班核心信息,通过链表管理所有航班:
typedef struct Flight {
int id; // 航班ID
char start[20]; // 出发地
char dest[20]; // 目的地
char takeoff[10]; // 起飞时间
char arrival[10]; // 到达时间
int remaining; // 剩余票数
float price; // 原价
float discount; // 会员折扣
struct Flight* next;
} Flight;
(3)用户结构体(User)
关联用户的账号、余额、订单链表:
typedef struct User {
char username[MAX_NAME]; // 用户名
char password[MAX_PWD]; // 密码
float balance; // 余额
Order* orders; // 订单链表头节点
struct User* next; // 用户链表节点
} User;
3. 整体流程设计
程序启动 → 加载文件数据(航班/用户)→ 主菜单选择(购票/管理员/退出)
├─ 购票模块:会员登录/注册 | 游客购票
├─ 管理员模块:登录验证 → 航班/用户管理
└─ 退出:释放内存 → 保存数据到文件
二、核心功能实现(新手重点掌握)
1. 文件持久化:数据不丢失的关键
新手容易忽略 “数据持久化”—— 程序退出后内存数据会消失,因此需要将链表数据写入文件,启动时再读取。本项目用二进制文件操作(fwrite/fread)实现:
(1)保存航班数据到文件
void saveFlightsToFile() {
FILE* fp = fopen(FLIGHT_FILE, "wb"); // 二进制写模式
if (fp == NULL) {
printf("文件打开失败!\\n");
return;
}
// 先写航班总数,再逐个写航班数据
int count = 0;
Flight* p = flightHead;
while (p != NULL) { count++; p = p->next; }
fwrite(&count, sizeof(int), 1, fp);
p = flightHead;
while (p != NULL) {
fwrite(p, sizeof(Flight), 1, fp);
p = p->next;
}
fclose(fp);
}
(2)从文件加载航班数据
void loadFlightsFromFile() {
FILE* fp = fopen(FLIGHT_FILE, "rb"); // 二进制读模式
if (fp == NULL) return;
freeFlightList(); // 先释放原有链表,避免内存泄漏
int count;
fread(&count, sizeof(int), 1, fp);
Flight* tail = NULL;
for (int i = 0; i < count; i++) {
Flight* newFlight = (Flight*)malloc(sizeof(Flight));
fread(newFlight, sizeof(Flight), 1, fp);
newFlight->next = NULL;
// 尾插法构建链表(保持数据顺序)
if (flightHead == NULL) {
flightHead = newFlight;
tail = newFlight;
} else {
tail->next = newFlight;
tail = newFlight;
}
}
fclose(fp);
}
2. 链表操作:动态增删改查
链表是 C 语言新手的重点和难点,以 “添加航班” 为例:
void addFlight() {
Flight* newFlight = (Flight*)malloc(sizeof(Flight));
if (newFlight == NULL) { // 新手必做:内存申请失败判断
printf("内存分配失败!\\n");
return;
}
// 自动生成航班ID(取现有最大ID+1)
newFlight->id = generateFlightId();
// 输入航班信息
printf("出发地:"); scanf("%s", newFlight->start);
printf("目的地:"); scanf("%s", newFlight->dest);
// … 其他信息输入
// 尾插法加入链表
newFlight->next = NULL;
if (flightHead == NULL) {
flightHead = newFlight;
} else {
Flight* p = flightHead;
while (p->next != NULL) p = p->next;
p->next = newFlight;
}
saveFlightsToFile(); // 新增后立即保存
}
3. 购票 / 退票核心逻辑
(1)会员购票(扣余额、减余票、生成订单)
void buyTicket(User* u, int isVip) {
showAllFlights(); // 先展示所有航班
int id, num;
printf("输入航班ID:"); scanf("%d", &id);
Flight* f = findFlightById(id, NULL);
if (f == NULL) { printf("航班不存在!\\n"); return; }
printf("购票数量:"); scanf("%d", &num);
if (num > f->remaining) { printf("余票不足!\\n"); return; }
// 计算价格(会员折扣)
float price = isVip ? f->price * f->discount : f->price;
float total = price * num;
// 会员购票扣余额
if (isVip && total > u->balance) {
printf("余额不足!\\n"); return;
}
if (isVip) u->balance -= total;
// 减余票、生成订单
f->remaining -= num;
addOrder(u, id, num, price, isVip); // 新增订单到用户链表
saveFlightsToFile();
}
(2)退票(加余票、退余额、修改订单)
核心是遍历用户订单链表,找到对应航班订单后修改数量 / 删除节点:
int removeOrder(User* u, int flightId, int refundQuantity) {
Order* prev = NULL;
Order* curr = u->orders;
while (curr != NULL) {
if (curr->flightId == flightId) {
if (refundQuantity > curr->quantity) return 0;
// 全额退票:删除节点;部分退票:修改数量
if (refundQuantity == curr->quantity) {
if (prev == NULL) u->orders = curr->next;
else prev->next = curr->next;
free(curr);
} else {
curr->quantity -= refundQuantity;
}
return 1;
}
prev = curr;
curr = curr->next;
}
return 0;
}
4. 菜单与权限控制
通过多层循环 + 分支实现菜单切换,新手需注意 “循环终止条件” 和 “权限校验”:
- 管理员登录:固定账号(admin/666666),3 次失败退出;
- 会员登录:遍历用户链表校验账号密码;
- 菜单循环:while(1) 配合 break 实现 “选择退出”。
三、新手避坑指南
1. 内存泄漏问题
- 每个 malloc 必须对应 free:项目中封装了 freeFlightList()、freeUserList()、freeOrders(),退出前统一释放;
- 避免重复加载文件时未释放原有链表(loadFlightsFromFile 中先调用 freeFlightList)。
2. 输入缓冲区问题
scanf 读取数字后,缓冲区会残留 \\n,导致后续 fgets 读取异常,因此封装了清空缓冲区函数:
void clearInputBuffer() {
int c;
while ((c = getchar()) != '\\n' && c != EOF);
}
3. 边界条件判断
- 购票数量不能为负、不能超过余票;
- 退票数量不能超过已购数量;
- 会员折扣限制在 0~1 之间(if (discount <0) discount=0;)。
四、项目扩展(新手进阶方向)
五、总结
这个航空售票系统是 C 语言新手的经典实战项目,核心是掌握:
- 结构体 + 链表的动态数据管理;
- 文件操作实现数据持久化;
- 函数封装降低代码耦合度;
- 边界条件和内存管理的编程习惯。
新手学习时建议分步骤实现:先搭结构体和链表框架 → 实现基础的增删改查 → 加入文件操作 → 完善权限和菜单逻辑。每实现一个功能就测试,避免一次性写完整套代码后难以调试。
通过这个项目,能彻底理解 C 语言 “面向过程” 的编程思想,也能为后续学习 C++、数据结构打下坚实基础。
通过网盘分享的文件:航空购票管理系统源码
链接: https://pan.baidu.com/s/1OX62Rbzslmas84unCiBXFA?pwd=yzhd 提取码: yzhd





