嵌入式全栈开发学习笔记---项目1:停车管理系统

目录

项目概述

程序文件规划

项目实现的功能

测试结果

 程序主流程图

程序详解(重点)

第一步:写一个欢迎界面welcome();

第二步:写一个主菜单界面menu();

第三步:获取用户输入的键值,并根据键值实现对应的功能

第四步:完成show_park()函数中第一个功能:查看场内停车情况

第五步:完成push_park()函数停车的功能

第六步:完成show_park()函数中第二个功能:查看所有车辆的信息

第七步:完成leave_park()函数中的第一个功能:找车

第八步:完成leave_park()函数中的第一个功能:离场

完整代码(共计大约700多行,几乎每句有注释)

main.c

show.c

park.c

data.c

 bfs.c

 park.h

data.h

本项目涉及知识补充

补充1:printf(“XXX”);和fprintf(stderr, “XXX”);的区别

补充2:使用printf打印颜色

补充3:如果嫌每次都要敲编译的命令太麻烦,可以创建一个Makefile的文件

补充4:time_t是什么数据类型?

补充5:头文件之间不能互相包含。

补充6:数组(名)之间不能相互赋值。

补充7:time(NULL)得到的时间怎么格式化?

补充8:计算时间difftime


前情知识链接:嵌入式全栈开发学习笔记_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

本篇就到这里,下篇继续!欢迎点击下方订阅本专栏↓↓↓

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vera工程师养成记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值