停车场管理系统课程设计:用栈、队列、链表一题通关

发布时间:2026/10/9 2:57:10
停车场管理系统课程设计:用栈、队列、链表一题通关 简介面向数据结构课程设计的学生这是一份以C语言实现的停车场管理系统完整资料源码基于链栈数据结构完成覆盖车辆入场、出场、增删查改等核心逻辑可自动计算停留时长与应收费用命令行终端界面经过优化兼顾课程设计代码实现与报告撰写需求。压缩包共40个文件大小约1.67MB主要包含cpp源码、exe可执行程序、sln/vcxproj工程文件、课程设计报告md/txt、调试生成的pdb/obj/tlog等文件目录结构清晰既适合直接运行演示也便于按模块阅读源码和二次修改。目前已有433人学习下载适合作为数据结构课程设计参考或期末项目模板。除核心功能外压缩包中的课程设计报告与大学编程作业说明可帮助快速理解设计思路配套的Visual Studio工程与可执行文件则降低了环境配置门槛拿到后即可运行查看效果。1. 停车场管理系统数据结构课程设计的经典选题一题练完栈、队列、链表数据结构课设题目翻来覆去就那么几个停车场管理系统属于那种“看着简单、拿高分难”的经典款。它不像表达式求值那样偏算法也不像图书管理系统那样偏工程它正好卡在中间用一个完整的业务场景把栈、队列、链表全串起来老师问数据结构选型时你每一问都有话可答。标题里“源代码可执行文件课程设计报告”三件套对应着你交作业时的三张脸——能跑、能讲、能扛问。这篇直接按课设交付标准拆解适合已经选题、或正在犹豫要不要拿它当主攻项目的同学。从数据结构选型讲到编译出 exe再到报告怎么写按顺序走完你手里就是一套完整的课设答案。2. 系统设计与数据结构选型栈、队列、链表各管一段2.1 需求拆解三个场景对应三类操作动手写代码之前先把业务拆干净。一个最基础的停车场管理系统无非三件事车子进场、车子出场、查车与收费。进场时如果场内还有空位直接停进去场内满了车就得在便道排队先来的先补位。出场时收费按停放时间算钱如果出场的车不在栈顶它上面的车得先挪走等它开出去之后再倒回来。这个“挪走再倒回”的动作天然就是栈的经典操作。拆到这一步选型就清楚了场内停车场用栈先进后出最里面的车最后走场外便道用队列先进先出排队的先入场。历史收费记录和在场车辆信息用链表或数组都行课设一般用单链表展示增删查能力。这正好覆盖了《数据结构》课里最核心的三种结构答辩时老师问你“为什么这里用栈不用队列”你就把需求往这一摆逻辑是闭环的。下面这张表可以直接写进报告的需求分析部分。业务场景数据结构核心操作为什么选它停车场内顺序栈入栈、出栈、遍历后进先出符合车实际进出顺序便道等待循环队列入队、出队、判满先进先出车位空了先进来的先补收费记录单向链表插入、查找、遍历记录数量不固定链式存储灵活车辆查询栈和队列遍历顺序查找数据量小遍历足够快2.2 停车场用顺序栈进出车就是入栈出栈停车场内部用顺序栈实现理由很实在课设规模小停车场容量固定顺序栈不需要动态申请内存指针操作少不容易写出野指针。链栈当然也能做但那是把简单问题复杂化除了在报告里多写一段话没有实际收益。栈的核心是三个状态量栈顶指针 top、容量 PARK_CAPACITY、数据数组。top 初始化为 -1 表示空栈入栈先自增再存数据出栈先取数据再自减。这里有个细节很多人翻车如果 top 初始化为 0容量 2 的场子只能停一辆车后面我会在避坑章节单独讲。结构体定义如下#define PARK_CAPACITY 2 // 停车场容量建议设小一点方便测试 #define QUEUE_CAPACITY 3 // 便道容量 typedef struct { char plate[12]; // 车牌号比如 B12345 int hours; // 进场时间时 int minutes; // 进场时间分 } Car; typedef struct { Car data[PARK_CAPACITY]; int top; // 栈顶指针-1 表示空 } ParkStack;PARK_CAPACITY 我故意在测试阶段设成 2这样能很快跑通“停车场满了进便道”的分支。等演示给老师看的时候再把容量改成 5 或者更大。plate 数组用 12 个字节课程设计级别的作业不需要纠结车牌长度够用就行。hours 和 minutes 不用 int 以外的类型因为后面计费要用整数计算分钟差避免浮点麻烦。2.3 便道用循环队列满场时的等待逻辑便道如果直接拿顺序队列实现会出现“假溢出”队头不断出队后rear 跑到数组末尾明明前面有空位却无法入队。所以必须用循环队列让 rear 和 front 在数组里绕圈。循环队列的经典做法是牺牲一个存储单元通过 (rear 1) % QUEUE_CAPACITY front 判断队满这样判满和判空不会混淆。typedef struct { Car data[QUEUE_CAPACITY]; int front; // 队头下标指向第一个有效元素 int rear; // 队尾下标指向下一个写入位置 } WaitQueue; int isQueueFull(WaitQueue *q) { return (q-rear 1) % QUEUE_CAPACITY q-front; } int isQueueEmpty(WaitQueue *q) { return q-front q-rear; } void enQueue(WaitQueue *q, Car car) { if (isQueueFull(q)) { return; } q-data[q-rear] car; q-rear (q-rear 1) % QUEUE_CAPACITY; } Car deQueue(WaitQueue *q) { Car car q-data[q-front]; q-front (q-front 1) % QUEUE_CAPACITY; return car; }QUEUE_CAPACITY 定义为 3但实际能排队的车是 2 辆因为牺牲了一个单元。这个“容量比预期少 1”的细节测试时特别容易看成 bug。如果你不想跟老师解释这个设计可以把 QUEUE_CAPACITY 定义成预期排队数加 1比如预期排 3 辆就定义成 4代码逻辑不变演示效果更直观。进场入场都走 enQueue 和 deQueue不要在 main 函数里直接操作 front 和 rear那样后续不好维护。2.4 收费记录与车辆信息链表还是数组课设报告里通常要求“车辆信息管理”这块我用单向链表做历史收费记录。原因很简单记录条数动态增长链表插入不需要搬移数据而且能展示节点的增删查。链表节点就两个字段一条 Car 记录和一个 next 指针外加一个收费金额字段。注意链表的头指针每次插入都更新到最新节点方便按时间逆序查看。typedef struct RecordNode { Car car; float fee; // 本次收费 struct RecordNode *next; } RecordNode;为什么场内不用链表因为停车场本身是栈硬拿链表模拟栈入栈出栈逻辑全变了老师追问一层就露怯。链表只负责“历史的”记录和当前的停车场状态解耦这样职责清晰。有的同学把在场的车也做成链表结果出场时要按位置删除节点还要处理临近结点代码量和出错概率直接翻倍得不偿失。数据的组织方式要跟着业务走不是为了炫技。3. 核心功能实现进场、出场、查询、计费的完整代码3.1 车辆进场入栈与排队的分支处理进场函数是整个系统的入口逻辑核心判断只有一句话停车场满没满。没满就入栈满了再看便道有没有位置有位置就入队。这里要处理一个隐藏问题输入车牌和时间时如果用户在同一个输入行里敲多了空格或者换行后面的操作会被缓冲区里的残留字符干扰所以我会在读取输入后立刻用 getchar() 清理一次。void enterPark(ParkStack *park, WaitQueue *queue) { Car car; printf(请输入车牌号如 B12345); scanf(%s, car.plate); printf(请输入进场时间时 分); scanf(%d %d, car.hours, car.minutes); getchar(); // 吃掉输入行末尾的换行 if (park-top PARK_CAPACITY - 1) { // 停车场未满直接入栈 park-top; park-data[park-top] car; printf(车辆 %s 进入停车场 %d 号位\n, car.plate, park-top 1); } else { // 停车场已满尝试进入便道 if (isQueueFull(queue)) { printf(停车场和便道均已满车辆 %s 无法停放\n, car.plate); return; } enQueue(queue, car); printf(停车场已满车辆 %s 进入便道等待\n, car.plate); } }判断停车场没满用的是park-top PARK_CAPACITY - 1而不是park-top ! PARK_CAPACITY - 1原因是程序里 top 只会从 -1 往上走不会越界但不排除后续改动出现意外赋值用小于号更安全。入栈时先自增再赋值顺序不能反否则第一个进场的车会被写到 data[0] 而不是 data[1]栈底空一格之后全部错位。便道已满时直接 return不要再往队里塞数据否则会覆盖队尾的车产生静默数据丢失。3.2 车辆出场临时栈倒库与计费逻辑出场是这套系统里最容易写崩的地方因为目标车不一定在栈顶。如果有一辆 B12345 要出场但停车场里有 3 辆车B12345 是最先停进去的它上面的两辆车得先挪走。挪到哪里临时栈。等 B12345 开走之后临时栈里的车按原顺序倒回。这个倒库过程用代码表达就是一个 while 循环加两个弹栈操作。void exitPark(ParkStack *park, WaitQueue *queue) { char plate[12]; Car tempStack[PARK_CAPACITY]; // 临时栈数组实现 int tempTop -1; int found -1; int exitHours, exitMinutes, totalMinutes; float fee; printf(请输入出场车牌号); scanf(%s, plate); getchar(); // 从栈顶往下查找目标车 for (int i park-top; i 0; i--) { if (strcmp(park-data[i].plate, plate) 0) { found i; break; } } if (found -1) { printf(未找到车辆 %s\n, plate); return; } // 目标车之上的车全部倒入临时栈 while (park-top found) { tempTop; tempStack[tempTop] park-data[park-top]; park-top--; } printf(请输入出场时间时 分); scanf(%d %d, exitHours, exitMinutes); totalMinutes (exitHours * 60 exitMinutes) - (park-data[park-top].hours * 60 park-data[park-top].minutes); if (totalMinutes 0) { totalMinutes 24 * 60; // 跨天停车按 24 小时补差 } // 计费基础 2 元超时部分每小时 1.5 元不足 1 小时按 1 小时算 fee 2.0f (totalMinutes / 60 (totalMinutes % 60 ! 0 ? 1 : 0)) * 1.5f; printf(车辆 %s 停放 %d 分钟收费 %.2f 元\n, park-data[park-top].plate, totalMinutes, fee); // 弹出目标车 park-top--; // 临时栈的车辆按原顺序倒回停车场 while (tempTop 0) { park-top; park-data[park-top] tempStack[tempTop]; tempTop--; } // 便道等待的第一辆车入场 if (!isQueueEmpty(queue)) { Car c deQueue(queue); park-top; park-data[park-top] c; printf(便道车辆 %s 已进入停车场\n, c.plate); } }临时栈直接用数组加一个临时栈顶指针不用封装成结构体因为它的生命周期只在 exitPark 这个函数内部封装反而增加阅读负担。计费这里我做了一个“不足一小时按一小时计”的向上取整每分钟 1.5/60 元会出现无法精确表示的小数但标准 C 的取整表达式可控不涉及浮点比较稳定性更好。跨天补差直接加 24*60 分钟不需要记录日期字段演示场景下够用。出队后立即补位要注意补位的车时间还是它进场时的时间停车场内不用重置时间收费按它真正停放的总时长算。3.3 按车牌查询与场内统计查询功能有两个常用场景按车牌号找车现在停在哪以及打印当前停车场和便道的占用情况。前者展示顺序查找后者展示遍历。查询必须同时扫栈和队列因为同辆车只会存在于其中一个位置。栈的遍历从栈底到栈顶队列的遍历从 front 走到 rear循环队列要借助取模计算长度不能直接用 rear 当长度。void searchCar(ParkStack *park, WaitQueue *queue) { char plate[12]; printf(请输入要查询的车牌号); scanf(%s, plate); getchar(); for (int i 0; i park-top; i) { if (strcmp(park-data[i].plate, plate) 0) { printf(车辆 %s 在停车场 %d 号位\n, plate, i 1); return; } } int len (queue-rear - queue-front QUEUE_CAPACITY) % QUEUE_CAPACITY; for (int i 0; i len; i) { int idx (queue-front i) % QUEUE_CAPACITY; if (strcmp(queue-data[idx].plate, plate) 0) { printf(车辆 %s 在便道第 %d 位\n, plate, i 1); return; } } printf(停车场和便道均未找到车辆 %s\n, plate); }查询的复杂度是 O(n)课设规模完全可以接受。栈遍历用 i 从 0 到 top输出位置时加 1因为车位编号从 1 开始更符合日常认知。便道遍历先算有效长度 len这一步很多初学会直接写queue-rear - queue-front万一 rear 已经绕回到 front 前面长度就是负数。取模公式是通用的建议直接背下来。查询后 return 退出函数避免同一个车牌被输出两次。如果你在报告里想展示“停车场无空位率”“排队人数”这类统计遍历时顺手加个计数器就行不需要单独写函数。4. 工程组织与可执行文件从源代码到双击运行的 exe4.1 模块划分头文件、实现文件、主程序代码全塞在一个 main.c 里当然也能跑但课设报告要求“模块化设计”你也得给自己留条后路如果老师在演示现场让你加一个功能单文件改动容易改出连锁 bug。我习惯拆成三个文件park.h 放结构体和函数声明park.c 放所有业务函数实现main.c 只放菜单和流程控制。这个分层对应报告里的“概要设计”和“详细设计”截图也好截。文件职责关键内容park.h常量、结构体、函数声明PARK_CAPACITY、Car、ParkStack、WaitQueuepark.c业务函数实现enterPark、exitPark、searchCar、队列操作main.c菜单循环、变量初始化main 函数、用户输入处理park.h 里用#ifndef PARK_H防止重复包含这个习惯在课设里不被要求但写了会让代码看着更专业。park.c 里每个函数的定义都要和 park.h 的声明保持一致参数类型和返回值不匹配时编译器只给警告不报错运行起来才是灾难。main.c 只负责调用不直接访问结构体内部字段如果 main.c 里出现park.top这样的操作说明封装没做好。4.2 编译命令与工程配置gcc 和 Dev-C 两条路生成可执行文件最直接的方式就是用 gcc 编译。如果你在 Linux 环境或者 Windows 的 MinGW 环境里打开终端进到源码目录执行下面这行命令就能生成 parking_system.exegcc main.c park.c -o parking_system.exe这条命令把 main.c 和 park.c 一起编译链接-o 指定输出文件名。如果只有一个 main.c 文件命令改成gcc main.c -o parking_system.exe就行。编译成功后Windows 下双击 parking_system.exe 就能运行但前提是你没有用到除标准库以外的第三方库。如果你用的是 Dev-C操作路径是文件 - 新建 - 项目 - Console Application然后把 park.h 和 park.c 添加进项目点编译运行。注意 Dev-C 默认用 GBK 编码保存源文件这在中文 Windows 下能正常显示但不要用 VS Code 的 UTF-8 编码去改源文件否则运行界面会出一堆乱码。如果你平时用 VS Code 写代码编译命令换成gcc main.c park.c -o parking_system.exe -finput-charsetUTF-8或者直接把代码里的所有中文提示改成英文彻底绕开编码问题。课程设计答辩现场绝大多数用的是 Dev-C 或 VS提前在演示机上编译一遍别到了现场才发现编译器版本不同导致链接报错。编译报错时先看第一条错误信息gcc 经常因为一个错误引发连锁报错修复第一个往往能消掉后面几十行。4.3 课程设计报告的结构与截图安排报告不是代码的堆砌是按“分析 - 设计 - 实现 - 测试”的逻辑讲清楚你为什么这么做。我通行的做法是六大块需求分析、概要设计、详细设计、调试与测试、总结与心得、参考文献。需求分析里放功能描述和业务流程图概要设计放模块划分表和数据结构的说明详细设计挑核心函数贴代码并加注释调试与测试放测试用例表格和运行截图。测试用例至少要覆盖三个场景正常进场出场、停车场满后进便道且出场后自动补位、查询不存在和不存在的车牌。每个测试用例配一张运行截图截图要显示完整窗口不要截一半。报告里代码的字体统一用等宽字体比如 Consolas如果是用 Word 写的代码段建议用“段落边框浅灰底纹”的方式排版老师一眼就能找到代码区域。总结部分写两三个你真正踩过的坑和解决办法比你大段抄教材更有说服力。参考文献列两本教材和一篇期刊就行不用堆数量。5. 课设常见问题排查5个必踩的坑和对应解法5.1 循环队列判满翻车队列满了还能进车吗现象停车场满后便道里明明还能再排一辆程序却提示“便道已满”把车拒之门外。 原因循环队列牺牲了一个存储单元来判断队满(rear 1) % QUEUE_CAPACITY front所以容量为 3 的数组实际只能存 2 辆车这是循环队列的固有特性不是 bug但非常容易和直觉冲突。 解决要么把 QUEUE_CAPACITY 定义成预期排队数加 1并在注释里写明“实际可用容量为 QUEUE_CAPACITY - 1”要么改用计数器方案维护一个 size 字段记录队列内元素个数判空为 0判满为 QUEUE_CAPACITY代价是维护 size 的代码会多一点。课设阶段我建议用第一种改动最小注释写清楚即可。5.2 栈顶指针边界top 的初始值决定了你能存几辆车现象PARK_CAPACITY 设为 2但停车场停一辆车后第二辆进不来或者更离谱的是第一辆停进去就报满。 原因top 初始化成 0入栈先存数据再执行 top判断满栈时用top PARK_CAPACITY听起来没问题但取出栈或遍历时边界直接乱套。本质是 top 的语义没统一它到底指向“栈顶元素位置”还是“下一个空位”。 解决统一用“top 指向栈顶元素”的语义初始化 top -1入栈先 top 再赋值判空 top -1判满 top PARK_CAPACITY - 1。这套约定在进场、出场、遍历三处保持一致写完后用容量为 1 的场景跑一遍边界进一辆、进第二辆、出第一辆、再进一辆四个动作全过一遍就稳了。5.3 scanf 残留换行菜单第二次输入直接跳过现象菜单输入 1 回车后进场函数里的“请输入车牌号”没等用户输入就一闪而过程序直接跳去执行下面的代码。 原因scanf(%d) 只消费了数字回车键产生的换行符还留在缓冲区下一个 scanf(%s) 读到这个换行符认为输入结束直接返回空值。 解决最简单粗暴的方法是每个 scanf 之后紧跟一句 getchar()把换行吃掉。我一般会在菜单的 scanf 后面加在函数内部的 scanf 后面也加养成习惯就不会复发。另一个方案是把格式化字符串开头加空格写成scanf( %s, car.plate)前导空格会让 scanf 自动跳过所有空白字符包括残留的换行。两个办法二选一不要同时用否则 getchar 会把下一次真正输入的第一个字符吃掉。5.4 浮点计费精度问题金额差一分钱就难看现象手算停车 90 分钟收费应该是 2 1.5 × 2 5 元程序输出 5.00 看着没错但连续停几辆后累计金额和打印结果对不上或者测试中发现 89 分钟和 90 分钟收费一样。 原因float 无法精确表示所有小数分钟数换算成小时再乘以 1.5 会产生二进制近似误差。printf(%.2f) 只是显示时四舍五入内部参与运算的仍然是近似值。 解决计费全部改用整数“分”计算避免浮点直接参与int feeFen 200 totalMinutes * 150 / 60;这里 totalMinutes 是整数乘 150 再除 60除法在整数域进行没有精度问题。输出时用printf(%d.%02d, feeFen / 100, feeFen % 100)把分拆成元和角分。课设答辩时老师可能故意问“停 1 分钟收多少钱”“停 59 分钟怎么算”用整数计算能给出稳定回答。5.5 中文乱码代码和报告里的文字各说各话现象在自己电脑上编译运行一切正常把 exe 拷到实验室电脑上中文提示全变成类似“鎴戠殑”的乱码或者报告从 Word 复制到另一个模板时中文变问号。 原因Windows 下 Dev-C 默认源文件编码是 GBK编译出的程序运行时按本地代码页解释中文字符串如果源文件被某个编辑器改成了 UTF-8编译器还在按 GBK 读字符对不上就乱码。跨机器拷贝 exe 时目标机器的系统区域设置不同也会触发。 解决坚持“代码在哪里编译就在哪里运行演示”。写代码时统一用 Dev-C 打开和保存不要混用 VS Code 和 Dev-C 轮流改文件。如果必须在 UTF-8 环境写代码用 gcc 时加-finput-charsetUTF-8编译参数。报告里的代码段要么直接从源码复制到 Word 并手动设置字体要么粘贴成纯文本后再排版不要从网页面板复制免得带入隐藏字符。6. 进阶让课设从“能跑”变成“抗追问”的四个升级点很多课设能跑但老师一问“你这个系统还能怎么改”就卡壳。我整理了四个高频追问对应的升级方向每个改动量都不大性价比很高。第一等待队列调度策略。现在便道是严格先进先出你可以扩展成“按车牌尾号优先”或“按 VIP 等级排队”用链表存储便道车辆按优先级插入。代码改动集中在 enQueue 附近报告里能多写一段算法设计。第二容量动态扩展。把顺序栈的固定数组改成指针运行时根据停车场实际大小动态申请内存。这一步会用到 realloc需要处理扩容失败的回滚逻辑。老师在课设答辩时最喜欢问“如果停车场容量临时增加怎么办”这个升级就是现成答案。第三数据持久化。程序退出前把在场车辆和累计收费写入文本文件启动时读取恢复现场。用 fscanf 和 fprintf 就能实现不用数据库。这个功能能让你在演示时展示“上次运行的数据还在”观感完全不同。第四接入系统时间。把手动输入进场时间改成调用time(NULL)获取当前系统时间程序运行更加真实也省去演示时来回敲时间的尴尬。这个升级要换掉 Car 里的 hours 和 minutes 为 time_t 类型涉及时间格式化输出属于标准的 C 标准库用法。我自己当年做这个课设时以为把代码跑通就万事大吉结果被老师追问“便道为什么只能停 3 辆”时支支吾吾说不上来。后来才明白课设的评分点从来不只在代码本身而在你对自己的每个设计决策是否有解释力。上面写的每一个坑都是我真金白银踩过的希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询