目录
第四步:完成show_park()函数中第一个功能:查看场内停车情况
第六步:完成show_park()函数中第二个功能:查看所有车辆的信息
第七步:完成leave_park()函数中的第一个功能:找车
第八步:完成leave_park()函数中的第一个功能:离场
补充1:printf(“XXX”);和fprintf(stderr, “XXX”);的区别
补充3:如果嫌每次都要敲编译的命令太麻烦,可以创建一个Makefile的文件
前情知识链接:嵌入式全栈开发学习笔记_Vera工程师养成记的博客-优快云博客
目前为止我们已经学习了linux系统的终端上的一些操作命令、vim编辑器上的一些操作命令、C语言和数据结构,接下来我们就用我们学过的这些知识做一个“停车管理系统”的项目。
PS:对项目有任何问题可进群询问:QQ交流群:963138186
项目概述
开发环境:基于VMware Workstation虚拟机上的Linux系统的终端上的vim编辑器
涉及重要知识点:
C语言(二维数组);
数据结构(链表、队列、广度优先算法(BFS));
程序文件规划
源文件:
main.c:主逻辑在该文件编辑
show.c:屏幕界面显示功能在该文件实现
park.c:关于车辆操作功能在该文件实现
data.c:队列和链表的操作功能在该文件实现
bfs.c:广度优先算法的功能在该文件实现
头文件:
park.h:关于车辆的操作函数和变量在该文件声明
data.h:关于队列和链表的操作函数和变量在该文件声明
项目实现的功能
该系统的主菜单界面有3个子菜单:1.查看停车场情况;2.停车;3.离开
子菜单1:查看停车场情况-->子功能:1.查看所有车辆信息;2.查看场内停车情况
子菜单2:停车
子菜单3:离开-->子功能:1.找车;2.离场
测试结果
启动欢迎界面
停留1s后显示主菜单
输入数字2回车,进入停车功能,输入车牌号回车(输入的车牌号需合法),系统随机生成一个位置,用户根据位置停放车辆成功
回车返到主菜单
输入数字1查看停车场情况,进入子菜单
输入数字1车辆信息
回车返回主菜单
输入数字1查看停车场情况,进入子菜单后输入数字2回车查看场内停车情况(停车的位置被标为了黄色)
回车返回主菜单
输入数字3进入离开的子菜单
输入数字1回车,输入车牌号回车,输入用户所在的当前位置(输入的位置需合法),系统提示人和车之间的最短路径
用户可以根据此路径找到自己的车
回车返回主菜单
输入数字3回车进入子菜单,输入数字2回车,输入车牌号回车可以看到停车时长和缴费信息,如果输入数字1表示已缴费则系统会提示车到出口的最短路径,再回车可以看到系统提示车辆已经离场。如果输入数字2表示放弃缴费则直接返回主菜单,不提示路径。
车辆离场后可以回车返回主菜单
输入数字1回车进入子菜单,输入数字2回车查看场内停车情况,可以看到刚刚停的那辆车已经不在了
下面为了测试停车场已停满的效果,所以把表示停车场的二维数组的行和列改小,现在最多可以停放9辆车
超出9辆车后,就会提示停车场已满,进入等候队列
如果此时有一辆车离场,系统会同时提示等候队列中的一辆车进场成功
程序主流程图
程序详解(重点)
第一步:写一个欢迎界面welcome();
第二步:写一个主菜单界面menu();
第三步:获取用户输入的键值,并根据键值实现对应的功能
第四步:完成show_park()函数中第一个功能:查看场内停车情况
查看场内停车情况在park_info()函数内完成
我们是用一个二维数组来模拟停车场,所以遍历数组这里我们需要先定义一个二维数组
为了方便我们后期根据需要随意更改行和列,我们需要宏定义行和列
然后将二维数组传过来给print_array()实现功能
这里我们要先布置停车场,道路用WAY表示,空车位用CAR表示,有车的车位用CAREXIST表示,然后根据不同的布置打印不同的符号
布置好停车场后,系统一开始要初始化一个空的停车场(即还没有车子进场的时候)
第五步:完成push_park()函数停车的功能
当用户输入车牌号的时候要判断车牌号的长度和是否重复
如果重复就提示输入有误,让用户重新输入,如果输入没有错误就进而判断停车场有没有满,
这里要定义一个统计实际在场车辆的变量和统计车位总数的变量,
如果Carnumber>Total则表示已满,就需要进入等待队列,在push_queue()函数中实现
但是停车这里我们得定义一个链式队列
然后创建一个等待队列(全局变量)
再初始化这个队列
接下来如果停车场没有满,则可以直接进场,在push_car()函数中实现
随机获取一个空的停车位在get_place()函数实现
同时我们还要把这个车的信息插入到链表中,在insert_link()函数中实现
第六步:完成show_park()函数中第二个功能:查看所有车辆的信息
查看场内所有车辆的信息在car_info()函数内完成
查看所有车辆的信息这里是用一个链表存储车辆的信息,所以我们首先得定义一个链表
然后创建一个指向头结点的指针
再初始化这个链表
然后开始遍历链表
第七步:完成leave_park()函数中的第一个功能:找车
用户要找车时输入自己人所在的位置和自己的车牌号,系统更具车牌号找到车的位置,在get_place_by_id()函数中实现
找到车后,系统就提示人到车的最短路径,在walk()函数中实现
这里我们需要保证我们的park二维数组不被乱改,所以把park二维数组中的东西都复制到map二维数组中,让map充当一个中间处理数组
要找最短路径就需要用到广度优先算法,广度优先算法要用到一个顺序队列,所以我们要另外定义一个队列bfs,然后初始化这个队列
因为停车场(二维数组)的行和列在后期是可以随意更改的,所以队列所需结点也是不固定的,因此需要宏定义大小
定义一个顺序队列
初始化这个队列
然后在bfs()函数中实现寻找最短路径的功能
从起点开始,走过的点就进队,进队在push()函数中实现
只要队列不为空就一直分析,判断队列是否为空在EmptyQueue()中实现
每次要分析一个起点可以往哪个方向走就让它出队,出队在pop()函数中实现
如果还不是终点就需要分析四个方向哪个能走,这个判断在is_OK()函数中实现,如果判断某个点可以走就让这个点进队
如果最后出队的点是终点,说明已经可以结束了,判断是否是终点在is_destination()中实现
如果是终点就可以打印路径了,打印路径在show_path()函数中实现
这里因为要直接在park数组中标注路径,所以我们要先把需要标记为路径的'-'记为有颜色的PATH,然后再把'-'恢复成没有颜色的WAY,因此我们需要宏定义这样一个变量:
第八步:完成leave_park()函数中的第一个功能:离场
用户需要离场时,输入自己的车牌号,系统根据车牌号找到车子的位置,然后根据车牌号获取车子进场的时间,获取进场时间在get_time_by_id()函数中实现
获取都到进场时间后就计算车子停留的时长,并且计算停车费用,提示用户缴费,用户缴费后,系统会自动提供一条车子到出口的最短路径。
车子离场后就将这个车子的信息从链表中删除,删除操作在delete_link()函数中实现
车子离场后如果等待队列中有车子出队
让它进场,进场成功系统提示车辆停放成功。
完整代码(共计大约700多行,几乎每句有注释)
main.c
#include <stdio.h>
#include "park.h"
#include "data.h"
//定义一个二维数组,作为停车场的地图
int park[ROW][COLUMN];
int Total;//车位总数
int CarNumber;//实际停放的车辆
Queue wait;//等待队列
LinkNode*head;//指向链表的头指针,存放链表的头节点的地址,即存放车辆信息的链表
int main()
{
int c;
//欢迎界面
welcome();
init_park();//初始化停车场-初始化为一个空的停车场
init_queue();//初始化队列
init_link();//初始化链表
while(1)
{
menu();//菜单选择界面
/*子菜单功能实现*/
scanf("%d", &c);//获取用户输入的键值
//实现键值对应的功能
switch(c)
{
case 1:
show_park();//查看停车场情况
break;
case 2:
push_park();//停车
break;
case 3:
leave_park();//离开
break;
default:
fprintf(stderr,"输入有误\n");
break;
}
}
return 0;
}
show.c
#include <stdio.h>
#include <unistd.h>//sleep的头文件
#include "park.h"
#include "stdlib.h"//system的头文件
//欢迎界面
void welcome()
{
system("clear");//清屏
printf("#######################################################\n");
printf("#######################################################\n");
printf("#######################################################\n\n");
printf("\t\t欢迎使用停车管理系统\n\n");
printf("#######################################################\n");
printf("#######################################################\n");
printf("#######################################################\n");
sleep(1);//睡眠1s
}
//菜单选择界面
void menu()
{
system("clear");//清屏
printf("#######################################################\n");
printf("#######################################################\n");
printf("#######################################################\n\n");
printf("\t\t 1.查看停车场情况\n\n");
printf("\t\t 2.停车\n\n");
printf("\t\t 3.离开\n\n");
printf("\t\t 请选择...\n");
printf("#######################################################\n");
printf("#######################################################\n");
printf("#######################################################\n");
}
//打印二维数组
void print_array(int array[ROW][COLUMN])
{
int i,j;
for(i=0;i<ROW;i++)
{
for(j=0;j<COLUMN;j++)
{
if(array[i][j]==WAY)//如果是道路就打印"-"
{
printf("- ");
}
else if(array[i][j]==CAR)//如果是空的车位就打印"#"
{
printf("# ");
}
else if(array[i][j]==CAREXIST)//如果是停了车的位置就打印红色的"#"
{
printf("\033[0;31m# \033[0m");
}
else if(array[i][j]==PATH)
{
printf("\033[0;31;43m- \033[0m");//如果是等于PATH就打印前景色红色的'-',背景色是黄色
}
}
printf("\n\n");
}
//让界面停留
getchar();
getchar();
}
park.c
#include "park.h"
#include <stdio.h>
#include <stdlib.h>
#include "data.h"
#include <time.h>//随机数的头文件
#include <string.h>//字符串处理函数的头文件
//声明一下某全局变量在别的文件中定义了
extern int park[ROW][COLUMN];
extern int Total;
extern int CarNumber;
//初始化停车场-初始化为一个空的停车场
void init_park()
{
int i,j;//分别控制二维数组的行和列的下标
for(i=0;i<ROW;i++)
{
for(j=0;j<COLUMN;j++)
{
//往二维数组中填写符号
if(i==0||i==ROW-1||j==0||j%3==0||j==COLUMN-1 && j%3!=1)
{
park[i][j]=WAY;//让二维数组的第一行和第一列/最后一行和最后一列/列的下标可以被3整除的地址作为道路/如果列是偶数个,即使是最后一列,也让它不是道路,比如说有20列,20%3不等于1,所以让它不是道路,如果是19%3=1,那就让它是道路
}
else
{
park[i][j]=CAR;//剩下的地方全部化为还没有停车的停车位
Total++;//统计车位总数
}
}
}
}
//查看所有车辆的信息
void car_info()
{
if(CarNumber==0)
{
printf("停车场没有车\n");
}
else
{
//只要遍历整个链表就行
traverse_link();
}
getchar();//界面停住,获取键值,遇到回车就结束
getchar();
}
//查看场内停车情况
void park_info()
{
//遍历二维数组
print_array(park);
}
//查看停车场情况
void show_park()
{
int c;
system("clear");
printf("1.查看所有车辆的信息:\n");
printf("2.查看场内停车情况:\n");
scanf("%d",&c);//获取键值
switch(c)
{
case 1:
car_info();//查看所有车辆的信息
break;
case 2:
park_info();//查看场内停车情况
break;
default:
fprintf(stderr,"输入有误\n");
break;
}
}
//在停车场中找到一个空闲的车位
Box get_place()
{
int row,column;
Box b;//返回的坐标
srand(time(NULL));
while(1)
{
//随机找一个位置
row=rand()%ROW;
column=rand()%COLUMN;
if(park[row][column]==CAR)//如果这个位置是空的
{
//就记录下这个位置的坐标返回
b.row=row;
b.column=column;
return b;
}
//如果这个if语句没有执行说明还没有找到空的位置,就跳不出while,就一直在找位置
}
}
//车场未满,让车进入
void push_car(char*id)
{
//先找一个空的停车位
Box b=get_place();
//如果已经找到一个空的位置,就把这个位置标记为CAREXIST
park[b.row][b.column]=CAREXIST;//表示车子开进去了
//然后把这个车的信息存放到一个链表里面
insert_link(id,b);
//一旦有车停放成功,就要累计一下
CarNumber++;
printf("车辆停放成功,位置:%d %d\n",b.row,b.column);
}
//停车
void push_park()
{
char id[32]={0};//存放车牌号
printf("请输入车牌号:");
scanf("%s",id);//获取车牌好
//判断长度是不是6,并且判断车牌号有没有重复--需要遍历链表
int length=strlen(id);
while(length!=6||id_is_repeat(id))//如果输入的长度不是6或者是重复的就一直循环
{
printf("输入的车牌号有误,请重新输入:");
scanf("%s",id);
length=strlen(id);//直到长度等于6才跳出循环
}
//判断停车场有没有停满
if(CarNumber>=Total)
{
printf("停车场已满,进入等待队列\n");
push_queue(id);//进队,先进者先出
}
else
{
//停车场没满,就让它进去
push_car(id);
}
//让界面停住
getchar();
getchar();
}
//根据车牌号和当前位置,规划最短路径
void find_car()
{
char id[32]={0};
int row,column;
printf("请输入车牌号:");
scanf("%s",id);
printf("请输入当前的位置:\n");
scanf("%d %d",&row, &column);//输入人所在的位置
//判断输入的位置是否有误
if(row<0||row>=ROW||column<0||column>=COLUMN)//如果小于0或者大于二维数组
{
fprintf(stderr,"输入的位置有误\n");
//让界面停住
getchar();
getchar();
return;
}
Box b=get_place_by_id(id);//通过车牌号找到位置
if(b.row==-1||b.column==-1)
{
fprintf(stderr,"输入的车牌号有误\n");
getchar();
getchar();
return;
}
//得到离开的路径
walk(row,column,b.row,b.column);//将人所在的位置和通过车牌号找到的行列传过去
}
//规划路线
void plan_route()
{
char id[32]={0};
int c;
printf("请输入车牌号:");
scanf("%s",id);//获取车牌号
Box b=get_place_by_id(id);//获取车的位置
if(b.row==-1||b.column==-1)
{
fprintf(stderr,"输入的车牌号有误\n");
getchar();
getchar();
return;
}
time_t start=get_time_by_id(id);
//计算停车时长
double park_time=difftime(time(NULL),start);//当前时间,开始时间
printf("停车时长%.1f 秒,请缴费 %f 元\n",park_time,park_time*0.01);
printf("1.已缴费 2.放弃缴费\n");
scanf("%d",&c);
switch(c)
{
case 1:
walk(b.row,b.column,ROW-1,COLUMN-1);//已缴费,规划路径离场
break;
case 2:
return;
}
//车离场后就把这个车牌号从数组中删掉
delete_link(id);
park[b.row][b.column]=CAR;//标记为空车位
CarNumber--;//实际车辆数目减1
printf("车辆已经离场\n");
if(!empty_queue())//如果等待队列中不等于1的话,即如果不为空
{
char id[32]={0};
//让先进入等候队列的车出队
pop_queue(id);//出队的同时把id保存下来
push_car(id);//进场
}
getchar();
}
//离开:1.找车,2.规划离开的路线
void leave_park()
{
int c;
system("clear");
printf("1.找车\n");
printf("2.离场\n");
scanf("%d",&c);
switch(c)
{
case 1:
find_car();
break;
case 2:
plan_route();
break;
default:
fprintf(stderr,"malloc failure\n");
break;
}
}
data.c
#include "data.h"
#include "stdlib.h"//malloc的头文件
#include "stdio.h"//fprintf的头文件
#include "string.h"//字符串处理函数的头文件
extern Queue wait;
extern LinkNode*head;//声明头指针在别的文件定义过了
/*等待队列*/
//初始化等待队列
void init_queue()
{
QueueNode*n=(QueueNode*)malloc(sizeof(QueueNode));//申请一个节点的空间
if(NULL==n)
{
fprintf(stderr,"malloc failure\n");//申请失败
return;
}
wait.front=wait.rear=n;//初始化时让队头和队尾都指向一个地址
}
//车场已满,车子进入等待队列
void push_queue(char *id)
{
//申请一个节点
QueueNode*n=(QueueNode*)malloc(sizeof(QueueNode));
if(NULL==n)
{
fprintf(stderr,"malloc failure\n");//申请失败
return;
}
//数组与数组之间是不能赋值的,所以要用strcpy进行拷贝
strcpy(n->id,id);//将输入的id拷贝到初始化队尾和队头指向的地方
//因为是尾插法,所以不用指向下一个节点
n->next=NULL;
//上一个节点的rear指向n的位置
wait.rear->next=n;
//然后让rear指向n
wait.rear=n;
}
/*存放车辆信息的链表*/
//链表初始化
void init_link()
{
head=(LinkNode*)malloc(sizeof(LinkNode));//申请一个头节点
if(NULL==head)
{
fprintf(stderr,"malloc failure\n");
return;
}
head->next=NULL;//头节点后面还没有节点
}
//等待的车出队
void pop_queue(char *id)
{
//链式队列要出队的话先把id保存下来
strcpy(id,wait.front->next->id);
QueueNode* n=wait.front->next;//把要删除的节点的地址记录下来,等会儿要释放它
wait.front->next=n->next;//然后把要删除的节点的下一个节点的地址给上一个节点的指针域
free(n);//释放掉要删除的节点
}
//判断等待队列是否为空
int empty_queue()
{
return (wait.front==wait.rear)? 1:0;
}
//插入车辆信息到链表
void insert_link(const char*id,Box b)
{
LinkNode*p=head;//将头指针的地址存放在p里,p指向了头节点
while(p->next)//只要p->next不为空,就一直循环向后移动
{
p=p->next;
}
//跳出while循环说明p已经指向了最后一个节点
//申请一个新的节点
LinkNode*n=(LinkNode*)malloc(sizeof(LinkNode));
if(NULL==n)
{
fprintf(stderr,"mallocfailure");
return;
}
//把id拷贝到新的节点
strcpy(n->id,id);
//插入时间
n->start=time(NULL);//这里只是存放了时间戳
n->locate=b;//插入位置
n->next=NULL;//因为尾插法后面没有节点了
//把新的节点的位置存放到上一个节点的指针域里面
p->next=n;
}
//遍历链表
void traverse_link()
{
LinkNode*p=head->next;//p指向第一个节点
while(p)//只要p不为空
{
printf("车牌号:%s\n",p->id);
printf("\t进场时间:%s\t停放的位置:%d %d\n",ctime(&(p->start)),p->locate.row,p->locate.column);
p=p->next;//p指向下一个节点
}
}
//通过用户输入的车牌号找车
Box get_place_by_id(const char *id)
{
Box b={-1,-1};//初值都是-1,如果找不到的话就返回这个初值
LinkNode *p=head->next;//p指向第一个节点
while(p)//只要p指向的不为空
{
//就把用户输入的id和已在场的车辆的车牌号匹配
if(strcmp(p->id,id)==0)
{
b=p->locate;
return b;//返回找到的坐标
}
p=p->next;//如果没有找到,就继续指向下一个节点
}
//如果循环结束了还是没有找到
return b;//返回初值
}
//删除离开的车辆的车牌号
void delete_link(const char*id)
{
LinkNode *fast=head->next,*slow=head;//fast指向第一个节点,slow指向头节点
while(fast)//只要fast不为空
{
//如果找到
if(strcmp(fast->id,id)==0)
{
slow->next=fast->next;//就把需要删除的节点的下一个节点的地址给上一个节点的指针域
//然后释放掉要删除的节点
free(fast);
return;
}
fast=fast->next;//如果没有找到就向后走
slow=slow->next;
}
}
//根据车牌号获得时间
int get_time_by_id(const char*id)
{
LinkNode *p=head->next;//指向第一个节点
while(p)//只要不为空
{
if(strcmp(p->id,id)==0)//如果找到
{
return p->start;//返回时间
}
p=p->next;//如果还没有找到就继续往后找
}
//如果跳出循环还没有找到就返回
return 0;
}
//判断车牌号是否重复
int id_is_repeat(const char *id)
{
LinkNode *p=head->next;//从第一个节点开始
while(p)
{
//只要p指向的地方不为空
if(strcmp(p->id,id)==0)//比较,如果等于0说明重复
{
return 1;//如果相等就返回1
}
p=p->next;//如果不相等p继续指向下一个节点
}
return 0;//如果全部不相等,就返回0
}
bfs.c
#include <stdio.h>
#include "park.h"
#define SIZE 4096
extern int park[ROW][COLUMN];//声明一下这个数组在别的文件中已经定义了
int map[ROW][COLUMN];//把park二维数组的东西复制到map中,作为一个中间操作数组
//节点
typedef struct _Box
{
int x;//点的行坐标
int y;//点的列坐标
int pre;//上一个节点的下标
}_Box;
//顺序队列
typedef struct Queue //广度优先算法要用的队列
{
_Box data[SIZE];//点
int front;//队头
int rear;//队尾
}Queue;
//初始化队列
void init_bfs(Queue *q)
{
q->front=q->rear=0;
}
//点进队
void push(Queue *q,_Box b)
{
//因为是顺序队列,所以要判断一下队是否已满
if(q->rear>=SIZE-1)
{
fprintf(stderr,"算法队列已满\n");
return;
}
q->data[q->rear++]=b;//将点的坐标赋值到rear指向的地方
}
//判断队列是否为空
int EmptyQueue(Queue* q)
{
return (q->rear==q->front)? 1:0;
}
//要分析的点出队
void pop(Queue*q,_Box *b)
{
if(q->front==q->rear)//如果队列为空
{
fprintf(stderr,"队列为空,无法出队\n");
return;
}
//保存这个点的值
*b=q->data[q->front++];//然后front++就相当于出队了
}
//判断是不是目的地
int is_destination(_Box b,int x,int y)
{
if((b.x-1==x && b.y==y) ||(b.x==x && b.y+1==y)||(b.x+1==x&&b.y==y)||(b.x==x&&b.y-1==y))//如果走到车子附近的点满足这四种条件即算终点
{
return 1;
}
else
{
return 0;
}
}
//判断下一个点能不能走
int is_OK(_Box b)
{
if(b.x>=0 && b.x<ROW && b.y>=0 && b.y<COLUMN && map[b.x][b.y]==WAY)//如果坐标是在二维数组的行列范围内并且是通道
{
return 1;
}
else
{
return 0;
}
}
//打印路径
void show_path(Queue*q,int front)
{
int i,tmp;
//先把是路径的点的下标标记为-1
for(i=front-1;i>=0;)
{
tmp=q->data[i].pre;//保存上一个点的下标
q->data[i].pre=-1;//把上一个点的下标标记为-1
i=tmp;//把上一个点的下标赋值个i;
}
//记录原来起点的状态
int state=park[q->data[0].x][q->data[0].y];//起点下面会被标记成PATH,先记录,等会儿要还原
//然后遍历一遍,把下标标记为-1的点全部打印出来,打印成红色的"-"
for(i=0;i<front;i++)
{
if(q->data[i].pre==-1)
{
park[q->data[i].x][q->data[i].y]=PATH;
}
}
//打印标了路径的park
print_array(park);
//还原没有标记路径的park
for(i=0;i<front;i++)
{
if(q->data[i].pre==-1)
{
park[q->data[i].x][q->data[i].y]=WAY;//从PATH恢复成WAY
}
}
//恢复起点的状态
park[q->data[0].x][q->data[0].y]=state;
}
void bfs(Queue *q,int x1,int y1,int x2,int y2)
{
_Box now,next;//创建点
//起点
now.x=x1;
now.y=y1;
now.pre=-1;//没有上一个点
//让起点进队
push(q,now);//将队列和起点传过去
//点进队后相当于这个点已经走过了,所以标记为-1
map[now.x][now.y]=-1;
while(EmptyQueue(q)!=1)//只要队列不为空就继续
{
//要分析一个点下一步可以往哪个方向走就让这个点出队
pop(q,&now);
if(is_destination(now,x2,y2))//如果最后出队的是终点的坐标就可以结束了
{
show_path(q,q->front);//打印路径
return;
}
//对出队的点坐一个分析
int dir;//方向,0向上,1向右,2向下,3向左
for(dir=0;dir<4;dir++)
{
//方向的坐标
switch(dir)
{
case 0:
next.x=now.x-1;
next.y=now.y;
break;
case 1:
next.x=now.x;
next.y=now.y+1;
break;
case 2:
next.x=now.x+1;
next.y=now.y;
break;
case 3:
next.x=now.x;
next.y=now.y-1;
break;
}
if(is_OK(next))//如果以上的坐标能走
{
next.pre=q->front-1;//就把刚刚出队的那个点的位置赋值给这个点的pre
push(q,next);//让这个点进队
map[next.x][next.y]=-1;//标记已经走过
}
}
}
}
void walk(int start_x,int start_y,int end_x,int end_y)//人所在的位置start,车辆所在的位置end
{
//创建队列
Queue queue;
//初始化队列
init_bfs(&queue);
//将park复制到map
int i,j;
for(i=0;i<ROW;i++)
{
for(j=0;j<COLUMN;j++)
{
map[i][j]=park[i][j];
}
}
//广度优先算法规划最短路径
bfs(&queue,start_x,start_y,end_x,end_y);//将队列的地址和起点终点的坐标传过去
}
park.h
#ifndef _PARK_H
#define _PARK_H
//可以根据自己的屏幕大小来修改数组park的行列值
#define ROW 9 //数组park的行, 定义为8行
#define COLUMN 18 //数组park的列, 定义为10列
#define WAY 1000 //表示通道
#define CAREXIST 1001 //表示有车存在
#define CAR 1002 //表示没有车存在
#define PATH 1003//最短路径的'-'
//存放车子的行和列坐标
typedef struct Box
{
int row;
int column;
}Box;
void welcome();
void menu();
void print_array(int array[ROW][COLUMN]);
void show_park();
void push_park();
void leave_park();
void init_park();
void car_info();
void park_info();
void walk(int start_x,int start_y,int end_x,int end_y);
#endif
data.h
#ifndef _DATA_H
#define _DATA_H
#include <time.h>//time_t的头文件
#include "park.h"
//定义链式队列的节点
typedef struct QueueNode
{
char id[16];//数据域,存放车牌好所以要用到数组
struct QueueNode *next;//指针域,存放下一个节点的地址,指向的空间存放的是一样的结构体
}QueueNode;
//定义链式队列
typedef struct Queue
{
//队列包含队头指针和队尾指针,本质也是一个节点
QueueNode* front;//队头
QueueNode* rear;//队尾
}Queue;
void init_queue();
void push_queue(char *id);
void pop_queue(char *id);
int empty_queue();
//存放车场内车的信息的链表
typedef struct LinkNode
{
char id[32];//保存车牌号
time_t start;//进场时间
Box locate;//停车的位置
struct LinkNode*next;//下一个节点地址
}LinkNode;
void init_link();
void insert_link(const char*id,Box b);
int id_is_repeat(const char *id);
void traverse_link();
Box get_place_by_id(const char *id);
void delete_link(const char *id);
int get_time_by_id(const char*id);
#endif
本项目涉及知识补充
补充1:printf(“XXX”);和fprintf(stderr, “XXX”);的区别
printf(“XXX”);输出到终端屏幕的时候,它会“憋住”,直到遇到换行符号\n才会一起输出。
而fprintf(stderr, “XXX”);是无缓冲的,会直接输出,不管有没有\n,都输出。相对来说,fprintf(stderr, “XXX”);更加适合紧急警告的情况。
补充2:使用printf打印颜色
格式:\033[显示方式;前景色;背景色m
显示方式 :0(默认值)、1(高亮)、22(非粗体)、4(下划线)、24(非下划线)、5(闪烁)、25(非闪烁)、7(反显)、27(非反显)
前景色:30(黑色)、31(红色)、32(绿色)、 33(黄色)、34(蓝色)、35(洋红)、36(青色) 、37(白色)
背景色:40(黑色)、41(红色)、42(绿色)、 43(黄色)、44(蓝色)、45(洋红)、46(青色)、47(白色)
示例:printf("\033[0;31m# \033[0m"); // m后面是自己要打印的字符
这句代码的意思是,显示方式是默认值,前景色是红色,没有背景色,要打印的字符是"# "
补充3:如果嫌每次都要敲编译的命令太麻烦,可以创建一个Makefile的文件
然后定义要编译的目标文件以及生成的二进制文件
Target=park
Object=main.o show.o park.o
$(Target):$(Object)
gcc $(Object) -o $(Target) -Wall
clean:
rm *.o $(Target)
保存好后我们就可以在终端直接输入make编译,然后./park查看运行结果
之后如果你只修改了.h文件,没有动.c文件的话,再输入make是不能编译的,我们要输入make clean把原来的一些中间文件清除掉再make编译就可以了
注:如果你又在当前目录下添加了别的.c文件,那么就可以直接在后面添加它的.o文件保存即可。
补充4:time_t是什么数据类型?
<time.h>中定义的 time_t 是 long int 类型(64位系统),或者 long long int 类型(32位系统)。
也就是说,time_t 数据类型始终占8个字节。简言之,时间值time_t 为长整型的别名。
补充5:头文件之间不能互相包含。
补充6:数组(名)之间不能相互赋值。
补充7:time(NULL)得到的时间怎么格式化?
调用time.h这个库里面的ctime函数就可以格式化,ctime函数的原型char *ctime(const time_t *timep); //把存放了time(NULL)生成的时间的变量的地址传参过去。效果就是这样的
补充8:计算时间difftime
函数原型:double difftime(time_t time1, time_t time2)
time1 -- 这是表示结束时间的 time_t 对象。
time2 -- 这是表示开始时间的 time_t 对象。
该函数返回以双精度浮点型 double 值表示的两个时间之间相差的秒数 (time1 - time2)。
在本项目中的应用
QQ交流群:963138186
本篇就到这里,下篇继续!欢迎点击下方订阅本专栏↓↓↓