ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

C语言停车场管理系统:栈与队列实现及课程设计避坑指南

C语言停车场管理系统:栈与队列实现及课程设计避坑指南 简介这份资源面向计算机相关专业学生与C语言初学者提供一套完整的数据结构课程设计参考方案解决停车场管理场景下的建模与编码实践问题。项目以链栈为核心数据结构实现了车辆进出登记、增删查改、停留时长计算与费用结算等逻辑终端界面简洁美观适合作为课程设计模板或数据结构练手项目。压缩包共40个文件约1.67MB包含cpp源代码、sln与vcxproj工程文件、exe可执行文件、pdb与idb调试符号、tlog与log构建日志以及课程设计报告文档覆盖从源码到运行验证的完整链路。目前已有433人学习下载读者可据此理解链栈在真实业务中的落地方式参考工程组织与调试配置并借助报告梳理设计思路与答辩要点快速完成课程设计任务。1. 停车场管理系统从数据结构课设到能跑起来的 C 语言工程很多人对「数据结构课程设计」的印象停留在纸面画几张链表图、写一段伪代码、凑一份实验报告交差。但停车场管理系统这个题目不一样它是少数能把栈、队列、链表、文件操作全部串起来还能真正跑出结果的项目。你停一辆车进去系统要判断车位够不够、该收多少钱、出场顺序对不对车走了历史记录还得落盘。这些动作背后全是数据结构的取舍。我见过太多同学卡在「逻辑想得通、代码写不出」这一步也见过把栈和队列用反了导致出场顺序错乱的翻车现场。这篇笔记就按一线做法把停车场管理系统的选型理由、核心代码、参数设置和踩坑点讲清楚让你能照着复现一套带源代码、可执行文件和课程设计报告的完整课设。2. 停车场管理系统的数据结构选型栈、队列还是链表2.1 为什么停车场天然适合「栈 队列」组合先想清楚停车场的物理规则。一条死胡同式的停车道先进去的车被后面的车堵住要出来必须等后面的车先挪开——这就是典型的后进先出对应栈Stack。而门口排队等车位的车辆先到先得是先进先出对应队列Queue。这两个结构不是随便挑的是业务规则直接决定的。我一般会把系统拆成三个数据区停车栈模拟停车道容量固定比如MAX_SIZE 5代表只有 5 个车位。等待队列车位满时后来的车进队列排队队头优先获得空位。让路栈当停车栈中间某辆车要出场时把它上面的车临时挪到让路栈等目标车开走后再把让路栈的车按原顺序压回停车栈。这里有个容易忽略的点让路栈压回停车栈时顺序必须严格还原否则车辆相对位置就乱了。用栈的push/pop天然满足这个还原关系这也是为什么不用数组硬模拟的原因——数组下标管理挪车逻辑代码量翻倍还容易错。选型上还有一层考虑课程设计通常要求体现「多种数据结构综合应用」。栈和队列是基础但如果只做固定容量链表就用不上。所以常见做法是让等待队列用链式队列实现节点动态malloc这样既练了指针又不受固定数组长度限制。严蔚敏版《数据结构》里链队列的经典写法可以直接搬过来用改改变量名就行。2.2 用 C 语言定义核心结构体与节点动手第一步是把数据结构用struct描述出来。下面这段代码是我常用的骨架字段命名尽量贴近业务方便后面写报告时对照。#include stdio.h #include stdlib.h #include string.h #include time.h #define MAX_SIZE 5 // 停车栈最大容量 #define RATE_PER_HOUR 5 // 每小时费率单位元 // 车辆信息 typedef struct { char plate[16]; // 车牌号 time_t enter_time; // 入场时间戳 } Car; // 顺序栈停车道 typedef struct { Car data[MAX_SIZE]; int top; // 栈顶下标-1 表示空 } SeqStack; // 链队列节点等待车辆 typedef struct QNode { Car car; struct QNode *next; } QNode; // 链队列 typedef struct { QNode *front; // 队头指针 QNode *rear; // 队尾指针 } LinkQueue;逻辑说明Car只存车牌和入场时间计费时用time(NULL) - enter_time算停留秒数再换算成小时。SeqStack用固定数组因为车位数量是物理约束没必要动态扩容。LinkQueue用带头尾指针的链式结构入队出队都是 O(1)。参数说明MAX_SIZE设成 5 是为了演示方便实际课设可以改成 10 或 20但要注意让路栈的容量也得同步否则挪车时可能溢出。RATE_PER_HOUR是计费单价报告里可以做成可配置项但代码里先用宏定义简单直接。2.3 初始化、入栈、出队三个基础操作的写法结构体定义完先把最基础的三个操作写出来后面所有业务都靠它们拼。// 初始化停车栈 void initStack(SeqStack *s) { s-top -1; } // 初始化链队列带头结点 void initQueue(LinkQueue *q) { q-front q-rear (QNode *)malloc(sizeof(QNode)); q-front-next NULL; } // 入栈停车 int push(SeqStack *s, Car c) { if (s-top MAX_SIZE - 1) return 0; // 栈满 s-data[(s-top)] c; return 1; } // 出栈取车 int pop(SeqStack *s, Car *c) { if (s-top 0) return 0; // 栈空 *c s-data[(s-top)--]; return 1; } // 入队排队 void enqueue(LinkQueue *q, Car c) { QNode *node (QNode *)malloc(sizeof(QNode)); node-car c; node-next NULL; q-rear-next node; q-rear node; } // 出队队头获得车位 int dequeue(LinkQueue *q, Car *c) { if (q-front q-rear) return 0; // 队空 QNode *p q-front-next; *c p-car; q-front-next p-next; if (q-rear p) q-rear q-front; // 最后一个节点出队 free(p); return 1; }逻辑说明push和pop用top下标控制注意(s-top)是先加后取(s-top)--是先取后减这两个写法别搞反。链队列出队时要处理「最后一个节点」的情况把rear拉回front否则rear会变成野指针下次入队直接崩。参数说明initQueue里malloc的头结点不存数据只用来统一空队和非空队的判断这是链队列的标准做法。如果不想用头结点front和rear初始都置NULL但入队出队要分情况讨论代码更啰嗦。我一般用带头结点版本省心。3. 车辆进出场与计费把业务逻辑落到代码上3.1 入场流程先判栈满再决定入栈还是入队入场逻辑的核心判断只有一句停车栈满了吗没满就push满了就enqueue。但实际写的时候还要考虑车牌重复、时间记录这些细节。// 车辆入场 void carEnter(SeqStack *s, LinkQueue *q) { Car c; printf(请输入车牌号); scanf(%15s, c.plate); c.enter_time time(NULL); if (push(s, c)) { printf(车辆 %s 已停入车位当前车位 %d/%d\n, c.plate, s-top 1, MAX_SIZE); } else { enqueue(q, c); printf(车位已满车辆 %s 进入等待队列\n, c.plate); } }逻辑说明scanf(%15s, c.plate)限制读入 15 个字符防止车牌输入过长导致缓冲区溢出这是 C 语言里fgets之外另一个常用防护手段。time(NULL)返回当前时间戳存进enter_time出场时直接相减。参数说明%15s里的 15 对应plate数组长度 16 减 1留一个字节给字符串结束符\0。这个细节在课设报告里可以写成「输入安全处理」是加分项。3.2 出场流程让路栈的挪车与还原出场是整个系统最复杂的部分。目标车可能不在栈顶需要把上面的车挪到让路栈取走目标车后再把让路栈的车压回停车栈。// 车辆出场 void carLeave(SeqStack *s, LinkQueue *q) { char plate[16]; printf(请输入要出场的车牌号); scanf(%15s, plate); SeqStack temp; // 让路栈 initStack(temp); Car c; int found 0; // 把目标车之上的车挪到让路栈 while (s-top 0) { pop(s, c); if (strcmp(c.plate, plate) 0) { found 1; break; } push(temp, c); } if (!found) { printf(未找到车辆 %s请检查车牌\n, plate); // 把挪走的车压回去 while (temp.top 0) { pop(temp, c); push(s, c); } return; } // 计费 long stay (long)(time(NULL) - c.enter_time); double hours stay / 3600.0; if (hours 1) hours 1; // 不足一小时按一小时计 double fee hours * RATE_PER_HOUR; printf(车辆 %s 出场停留 %.1f 小时费用 %.2f 元\n, c.plate, hours, fee); // 让路栈的车压回停车栈 while (temp.top 0) { pop(temp, c); push(s, c); } // 等待队列队头补位 if (dequeue(q, c)) { push(s, c); printf(等待车辆 %s 已补入车位\n, c.plate); } }逻辑说明挪车循环里每pop一辆车就判断是不是目标车是就break不是就压入让路栈。找不到车时必须把让路栈的车全部压回否则停车栈数据就丢了。计费部分stay / 3600.0得到小时数不足一小时按一小时算这是停车场常见规则。参数说明hours 1的判断可以改成ceil函数但需要math.h课设里手动判断更直观。RATE_PER_HOUR如果要做成分时段计费可以在这里加if判断当前时间但基础版先用固定费率。3.3 文件持久化把车辆记录写进文本文件课设通常要求数据能保存下次运行还能查历史。最简单可靠的做法是追加写文本文件每行一条记录。// 保存出场记录 void saveRecord(Car c, double fee) { FILE *fp fopen(record.txt, a); if (fp NULL) { printf(记录文件打开失败\n); return; } time_t now time(NULL); fprintf(fp, %s %ld %.2f\n, c.plate, (long)now, fee); fclose(fp); } // 读取历史记录 void showRecords() { FILE *fp fopen(record.txt, r); if (fp NULL) { printf(暂无历史记录\n); return; } char plate[16]; long t; double fee; printf(车牌\t\t出场时间\t\t费用\n); while (fscanf(fp, %15s %ld %lf, plate, t, fee) 3) { printf(%s\t\t%s\t\t%.2f\n, plate, ctime(t), fee); } fclose(fp); }逻辑说明fopen用a模式追加不会覆盖旧记录。fscanf返回值判断是否读到 3 个字段防止文件末尾空行导致死循环。ctime把时间戳转成可读字符串但注意它返回的字符串自带换行符输出格式会有点乱课设里可以手动格式化。参数说明record.txt放在可执行文件同目录路径用相对路径避免绝对路径在不同机器上失效。如果要做成「源代码 可执行文件」一起交记得把record.txt清空或删掉不然老师运行时会看到你的测试数据。4. 停车场管理系统避坑5 个血泪教训4.1 让路栈压回顺序错乱车辆凭空消失现象出场操作后停车栈里的车少了一辆或者顺序和原来不一样。原因让路栈压回时用了错误的循环条件比如while (temp.top 0)漏掉了下标 0 的元素或者压回时又判断了一次车牌导致目标车被重复压入。解决压回循环统一用while (temp.top 0)先pop再push中间不做任何条件判断。挪车和还原是两个独立阶段还原阶段只负责搬运。4.2 链队列出队后 rear 指针悬空现象等待队列最后一辆车出队后再入队新车辆时程序崩溃。原因出队时只改了front-next没有判断出队节点是不是rear指向的节点。当队列只剩一个节点时rear还指向被free掉的内存。解决出队后加一句if (q-rear p) q-rear q-front;把rear拉回头结点。这是链队列的标准收尾动作漏了必崩。4.3 车牌输入带空格导致读取截断现象输入「京 A12345」这种带空格的车牌程序只读到「京」后面全乱。原因scanf(%s)遇到空格就停止读取车牌被截断。解决车牌号统一要求不含空格或者改用fgets读整行再去掉换行符。课设里我一般直接规定车牌不含空格输入提示里写清楚省去处理麻烦。4.4 时间计算出现负数现象计费时显示停留时间为负费用也是负的。原因enter_time没初始化或者结构体拷贝时丢了时间字段导致time(NULL) - enter_time算出负数。解决入场时务必c.enter_time time(NULL)并且在push和enqueue里传的是整个Car结构体不是指针避免浅拷贝问题。如果用了指针要确保指向的数据在出队前不被释放。4.5 文件读写后忘记 fclose现象程序运行几次后record.txt内容不完整或者下次打开文件失败。原因fopen后没有fclose缓冲区数据没刷到磁盘文件句柄也没释放。解决每个fopen后面必须配对fclose哪怕中间有return也要先关文件。可以用goto统一清理但课设里直接在每个分支写fclose更直观。5. 课程设计报告与可执行文件的打包技巧5.1 报告里必须写清楚的三个数据结构图课程设计报告不是代码注释的堆砌老师最想看的是你对数据结构的理解。我一般会在报告里放三张图停车栈的入栈出栈示意图、链队列的入队出队指针变化图、让路栈的挪车还原流程图。这三张图不用多精美用 Word 自带的形状工具画就行关键是标清楚top、front、rear指针的变化。报告结构可以按「需求分析 → 数据结构设计 → 核心算法描述 → 测试用例 → 心得体会」来写。测试用例部分要覆盖正常入场、车位满排队、中间车辆出场、队列补位、历史记录查询。每个用例写输入、预期输出、实际输出截图贴上去。5.2 可执行文件怎么编译和交付用 GCC 编译的命令很简单gcc parking.c -o parking -Wall-Wall打开所有警告课设代码里常见的未初始化变量、类型不匹配都能提前发现。Windows 下如果用的是 Dev-C 或 CodeBlocks直接点编译生成.exe就行但记得把record.txt一起放进交付文件夹。交付文件夹建议这样组织文件/文件夹说明parking.c源代码带注释parking.exe可执行文件Windows 下双击可运行record.txt空文件或删除避免测试数据干扰课程设计报告.doc报告文档含截图和流程图README.txt编译命令和运行说明5.3 答辩时老师最爱问的三个问题第一个「为什么用栈不用数组」答栈的push/pop天然匹配停车道的后进先出数组需要手动管理下标代码复杂且容易越界。第二个「等待队列为什么用链表」答队列长度不确定链表动态分配内存不受固定数组长度限制。第三个「让路栈的作用是什么」答临时存放被挪车辆保证目标车出场后其他车顺序不变。这三个问题答顺了课设基本就稳了。我自己的习惯是答辩前把代码里的关键行号标出来老师问到哪就翻到哪比现场找代码从容得多。希望帮到你。本文还有配套的精品资源点击获取
返回列表