用C语言实现简单的车票预定系统

引言

很多人在学习数据结构的时候感觉抽象至极,其实把数据结构和编程语言分开来学可以极大减少这些抽象。前篇我们介绍了用C语言去实现一个基础的数据结构(线性表->动态数组)。本片内容,我将和大家分享一个简单而实用的车票预定系统的实现。全程非常简单,只用到了编程中的基础知识(数组、函数、结构体、指针、控制语句),为新手小白量身打造,开始上车吧。


目录

引言

代码结构讲解

结构体定义: booking 和 booking_list  

创建订单列表并进行初始化(create_list)

向列表中添加一个新的订单(append_booking)

在列表中查找一个订单(find_booking) 

 在列表中删除一个订单(delete_booking)

 打印列表中所有订单(print_list)

删除列表,释放内存(delete_list) 

主函数(main)测试

 总结


代码结构讲解

该部分提供代码的整体结构,并对各个模块进行逐步解释,通俗易懂。(代码实现方式有很多种,此次完整代码在最后,鼓励创新)/(代码不算高级,应对学校作业绰绰有余)

结构体定义: bookingbooking_list  

1.定义常量 

 // 定义姓名和火车号的最大长度 
 #define MAX_NAME_SIZE 30
 #define MAX_TRAIN_LEN 20

首先,我们定义了一些常量,这些常量用于设置姓名和火车号的最大长度。使用常量的好处在于,它使代码更易于维护。如果将来需要更改长度,只需修改这里的数值,而无需在代码中到处查找。

2.定义订单结构体(booking) 

 // 创建一个结构体,包含订单的基本信息;
 typedef struct
 {
	char name[MAX_NAME_SIZE];
	char train_number[MAX_TRAIN_LEN];
	int seat_number; 
 }booking;
 

结构体是C语言中一种用于组合不同数据类型的方式,非常适合用于表示复杂数据。该结构体包含三个成员,表示订单的基本信息。

  • char name[MAX_NAME_SIZE];:存储乘客的姓名,最多可存储 MAX_NAME_SIZE 个字符。

  • char train_number[MAX_TRAIN_LEN];:存储火车号,最多可存储 MAX_TRAIN_LEN 个字符。

  • int seat_number;:存储座位号,使用整数类型表示。

3.定义订单列表结构体 (booking_list)

 // 定义一个结构体,作存储订单的列表;
 typedef struct
 {
 	booking* bookings;
 	size_t size;
 	size_t capacity;
 }booking_list;
 

该结构体(如果看不懂,可以观看主页关于动态数组的那篇先做了解)包含三个成员,用于存储多个订单的列表。

  • booking* bookings;:这是一个指向 booking 结构体的指针数组,动态存储多个订单。这种设计使得我们可以灵活地扩展订单数量。

  • size_t size;:当前列表中订单的数量。size_t 是一种无符号整数类型,适用于表示大小和数量。

  • size_t capacity;:当前列表的容量,即可以存储的最大订单数量。这使得我们能够在需要时进行内存重新分配,以适应新的订单。

以上部分代码展示了如何通过结构体和常量定义基本的数据模型。理解这些基础知识是学习更复杂编程概念的关键。接下来,我们将讨论如何通过这些结构体实现具体的功能。 

创建订单列表并进行初始化(create_list)

 // 创建一个订单列表并进行初始化;
 booking_list* create_list(size_t initial_capacity)
 {
 	booking_list* list = (booking_list*)malloc(sizeof(booking_list));
 	if(list == NULL)
 	{
 		printf("Error:Failed to allocate memory for the booking list structure.\n");
 		return NULL;
	 }
	list->bookings = (booking*)malloc(initial_capacity * sizeof(booking));
	if(list->bookings == NULL)
	{
		printf("Error:Failed to allocate memory for the internal booking array.\n");
		free(list);
		return NULL;
	}
	list->size = 0;
	list->capacity = initial_capacity;
	return list;
 }

在这一段代码中,创建了一个函数 create_list,用于创建并初始化一个订单列表。下面是对每一部分的详细解释。

1.函数定义

  • booking_list*:函数的返回值类型是一个指向 booking_list 结构体的指针,表示函数将返回一个新创建的订单列表。

  • size_t initial_capacity:该参数表示订单列表的初始容量,用于预分配内存。size_t 是一种适用于表示大小的无符号整数类型。

2.内存分配

  • malloc(sizeof(booking_list)):分配足够存储一个 booking_list 结构体的内存。

  • (booking_list*):将返回的 void* 类型指针强制转换为 booking_list* 类型,以便我们可以通过它访问 booking_list 的字段。

3.检验内存分配(两次)

  • 在分配内存后,我们检查返回值是否为 NULL。如果为 NULL,这意味着内存分配失败,我们打印错误信息并返回 NULL。这样做是为了确保程序的健壮性,避免后续操作造成崩溃。

  • 同样,我们检查 bookings 的内存分配是否成功。如果失败,打印错误信息,释放先前分配的 list 结构体的内存,然后返回 NULL。 

4.初始化并返还指针

  • 如果所有内存分配都成功,我们将 size 初始化为 0,表示当前没有订单。同时将 capacity 设置为传入的初始容量,以便后续添加订单时使用。

  • 最后,函数返回指向新创建的 booking_list 结构体的指针,使得调用者能够访问和操作这个订单列表。 

通过这个函数,我们实现了动态创建和初始化一个订单列表的功能,掌握了如何使用内存分配以及如何确保内存分配成功。这是构建更复杂数据结构和功能的基础。接下来,我们将介绍如何向订单列表中添加新订单。  

向列表中添加一个新的订单(append_booking)

在这一段代码中,我们实现了一个名为 append_booking 的函数,用于向订单列表中添加新的订单。下面是对每个部分的详细解释。

 1.函数定义

 int append_booking(booking_list* list, const char* name, const char* train_number, int seat_number) 
  • int:函数的返回值类型是整数,通常用来表示成功与否(0表示成功,-1表示失败)。

  • booking_list* list:这是指向订单列表的指针,函数通过这个指针访问和修改列表中的内容。

  • const char* name:乘客的姓名,使用 const 修饰表示该字符串在函数内部不会被修改。

  • const char* train_number:火车号,同样使用 const 修饰。

  • int seat_number:座位号,用整数表示。

2.检查并扩容

 	if(list->size >= list->capacity)
 	{
 		size_t new_capacity = list->capacity * 2;
 		booking* new_bookings = (booking*)realloc(list->bookings, new_capacity * sizeof(booking));
 		if(new_bookings == NULL)
 		{
 			printf("Error:Failed to reallocate memory for the interal booking array.\n");
 			return -1;
		 }
		list->bookings = new_bookings;
		list->capacity = new_capacity;
	 }

在添加新订单之前,我们首先检查当前列表的大小是否达到了容量限制:

  • if(list->size >= list->capacity):如果当前订单数量等于或超过容量,就需要扩展内存。

  • size_t new_capacity = list->capacity * 2;:将新的容量设置为当前容量的两倍,以便为将来的订单预留空间。

  • realloc:用于重新分配内存。我们使用 realloc 函数来扩展之前为 bookings 分配的内存。如果内存重新分配失败,realloc 会返回 NULL

  • 如果 new_bookingsNULL,表示内存分配失败,打印错误信息并返回 -1

通过 append_booking 函数,我们实现了向订单列表添加新订单的功能,并有效地管理内存。这段代码展示了如何处理动态内存分配和字符串操作,初学者在理解这些基本概念的同时,也能掌握如何安全地管理数据。接下来,我们将介绍如何在列表中查找一个订单。 

在列表中查找一个订单(find_booking) 

在这一段代码中,我们实现了一个名为 find_booking 的函数,用于在订单列表中查找特定的订单。以下是对每一部分的详细解释。 

 // 在列表中查找一个订单;
 int find_booking(booking_list* list, const char* name, const char* train_number, int* seat_number)
 {
 	// 遍历列表;
	for(size_t i = 0; i < list->size; i++)
	{
		if(strncmp(list->bookings[i].name, name, MAX_NAME_SIZE) == 0 && strncmp(list->bookings[i].train_number, train_number, MAX_TRAIN_LEN) == 0)
		{
			*seat_number = list->bookings[i].seat_number;
			return 0;		// 比较前两个字符,如果相同返回 0; 
		}
	 } 
	return -1;		//未找到匹配项,查找失败,返回 -1; 
  } 

 1.定义函数

该函数包含四个参数

  • booking_list* list:指向订单列表的指针,函数通过这个指针访问存储的订单。

  • const char* name:乘客的姓名,使用 const 修饰表示该字符串在函数内部不会被修改。

  • const char* train_number:火车号,同样使用 const 修饰。

  • int* seat_number:指向整数的指针,用于存储查找到的座位号。

2.遍历列表

使用一个 for 循环遍历整个订单列表:

  • size_t i = 0;:初始化循环变量 i

  • i < list->size;:循环条件,确保只遍历现有的订单。

  • i++:每次迭代增加 i,以便检查下一个订单。

3.比较订单信息

在循环体内,我们使用 strncmp 函数比较当前订单的姓名和火车号是否与输入的参数相等:

  • strncmp(list->bookings[i].name, name, MAX_NAME_SIZE):比较两个字符串的前 MAX_NAME_SIZE 个字符。如果相等,返回 0。

  • &&:逻辑与运算符,确保两个条件同时满足,即姓名和火车号都匹配。

通过 find_booking 函数,我们实现了在订单列表中查找特定订单的功能。这个过程展示了如何遍历数组、比较字符串,以及如何通过指针返回查找结果。理解这些基本操作对于编写有效的搜索算法至关重要。接下来,我们将介绍如何从订单列表中删除一个订单。 

在列表中删除一个订单(delete_booking)

在这一段代码中,我们实现了一个名为 delete_booking 的函数,用于从订单列表中删除特定的订单。以下是对每一部分的详细解释。 

// 在列表中删除一个订单;
int delete_booking(booking_list* list, const char* name, const char* train_number)
{
	// 遍历列表;
	for(size_t i = 0; i < list->size; i++)
	{
		if(strncmp(list->bookings[i].name, name, MAX_NAME_SIZE) == 0 && strncmp(list->bookings[i].train_number, train_number, MAX_TRAIN_LEN) == 0)
		{
			for(size_t j = i; j < list->size - 1; j++)
			{
				list->bookings[j] = list->bookings[j + 1];
			}
			list->size--;
			return 0;
		}
	 } 
	return -1;
 } 
 

 1.函数定义

  • booking_list* list:指向订单列表的指针,函数通过这个指针访问和修改列表中的内容。

  • const char* name:乘客的姓名,使用 const 修饰表示该字符串在函数内部不会被修改。

  • const char* train_number:火车号,同样使用 const 修饰。

2.查找匹配的订单

在循环体内,我们使用 strncmp 函数比较当前订单的姓名和火车号是否与输入的参数相等:

  • strncmp(list->bookings[i].name, name, MAX_NAME_SIZE):比较两个字符串的前 MAX_NAME_SIZE 个字符。如果相等,返回 0。(同上)

3.删除订单更新列表

一旦找到匹配的订单,我们需要将其删除并更新后续的订单:

  • 使用另一个 for 循环,从当前匹配的订单位置 i 开始,将后续的每个订单向前移动一位,从而覆盖掉要删除的订单。

  • list->bookings[j] = list->bookings[j + 1];:将下一个订单的内容复制到当前的位置。

  • 最后,我们将 size 减少 1,表示订单数量减少。

通过 delete_booking 函数,我们实现了从订单列表中删除特定订单的功能。代码展示了如何遍历数组、比较字符串,以及如何更新数组以保持正确的顺序。这些操作对管理数据结构非常重要,初学者在理解这些概念后,能够更好地处理各种数据操作。接下来,我们将介绍如何打印订单列表中的所有订单。 

打印列表中所有订单(print_list)

在这一段代码中,我们实现了一个名为 print_list 的函数,用于打印订单列表中的所有订单。以下是对每一部分的详细解释。 

 // 打印列表中所有订单;
 void print_list(booking_list* list)
 {
 	for(size_t i = 0; i < list->size; i++)
 	{
 		printf("Name:%s, Train number:%s, Seat number:%d \n", list->bookings[i].name, list->bookings[i].train_number, list->bookings[i].seat_number);
 		
	 }
  } 
 

没什么好解释的吧?看不懂建议直接回炉重造->谁教的你找谁去。如下:

在循环体内,使用 printf 函数输出当前订单的信息:

  • Name:%s:格式化字符串,用于输出乘客的姓名。

  • Train number:%s:格式化字符串,用于输出火车号。

  • Seat number:%d:格式化字符串,用于输出座位号,%d 表示输出一个整数。

list->bookings[i].namelist->bookings[i].train_numberlist->bookings[i].seat_number 分别获取当前订单的姓名、火车号和座位号,并将它们插入到输出字符串中。

删除列表,释放内存(delete_list) 

在这一段代码中,我们实现了一个名为 delete_list 的函数,用于删除订单列表并释放所占用的内存。

 // 删除列表,释放内存;
 void delete_list(booking_list* list)
 {
 	free(list->bookings);
 	list->bookings = NULL;
 	free(list);
 	// list = NULL;    可以不要这一步; 
  } 
  

 先释放数组内存,再释放列表本身内存。

  • free 函数用于释放之前为 bookings 动态分配的内存。

  • 这一步是非常重要的,因为如果不释放内存,可能会导致内存泄漏,特别是在长时间运行的程序中。

  • bookings 指针设置为 NULL,确保指向的内存不再有效。这是一个好的编程实践,可以防止野指针。

  • 再次使用 free 函数释放为 list 结构体本身分配的内存。这样可以确保整个数据结构的内存都被正确释放,防止内存泄漏。

  • 最后这一行注释说明,虽然将指针设置为 NULL 是一个常见的做法,但在这个上下文中并不必要,因为函数内的 list 是一个局部变量。修改这个局部变量并不会影响调用函数时传入的指针。

通过 delete_list 函数,我们实现了释放订单列表及其内存的功能。这段代码展示了内存管理的重要性,特别是在使用动态分配内存时。理解如何正确释放内存是防止内存泄漏和保持程序稳定的关键。接下来,我们将进入主函数部分,展示如何测试之前实现的功能。 

主函数(main)测试

1.创建订单列表

booking_list* list = create_list(5);
  • 通过 create_list 函数创建一个新的订单列表,初始容量为 5。此时 list 是指向 booking_list 结构体的指针,用于存储订单信息。 

2.添加订单

append_booking(list, "Morandi", "G9673", 12);
append_booking(list, "Morandi_01", "G762", 3);
append_booking(list, "Morandi_02", "G8940", 5);
append_booking(list, "Morandi_03", "K472", 14);
  • 使用 append_booking 函数添加几条订单到列表中。每条订单包含乘客姓名、火车号和座位号。 

3.打印所有订单

puts("========================================="); 
puts("所有订单如下:");
print_list(list);
  • 输出分隔符后,调用 print_list 函数打印当前列表中所有的订单信息,便于查看。 

4.查询特定订单

int seat_number;
if(find_booking(list, "Morandi", "G9673", &seat_number) == 0)
{
    printf("Name:Morandi, Train_number:G9673, Seat_number:%d.\n",seat_number);
}
else
{
    printf("Booking not found.\n");
}
  • 定义一个整型变量 seat_number 用于存储查找结果。

  • 调用 find_booking 函数查找名为 "Morandi"、火车号为 "G9673" 的订单。

  • 如果查找成功,输出对应的座位号;如果未找到,输出相应提示。

5.删除特定订单

if(delete_booking(list, "Morandi_01", "G762") == 0)
{
    printf("Booking deleted.\n");
} 
else
{
    printf("Failed to delete booking.\n");
}
  • 调用 delete_booking 函数删除名为 "Morandi_01"、火车号为 "G762" 的订单。

  • 输出删除成功或失败的提示。

6.打印修改后的订单

puts("========================================="); 
puts("修改之后的订单如下:");
print_list(list);

7.删除订单释放内存

delete_list(list);

8.结束程序 

return 0;

main 函数中,我们综合使用了之前实现的功能,展示了如何创建、添加、查询、删除和打印订单。这个完整的流程让初学者能够清晰地看到每个功能的实际应用,并理解如何通过不同的函数进行数据管理。通过这些示例,读者可以学习到基本的动态内存管理、数据结构操作以及如何组织代码,以实现一个简易的车票预定系统。 


总结

本文深入探讨了一个简单的车票预定系统的实现过程,使用 C 语言构建了一个易于理解的订单管理程序。通过对每一部分代码的详细讲解,初学者们能够了解到动态内存管理、数据结构的使用以及基本的输入输出操作。 完整代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

 // 定义姓名和火车号的最大长度 
 #define MAX_NAME_SIZE 30
 #define MAX_TRAIN_LEN 20

 // 创建一个结构体,包含订单的基本信息;
 typedef struct
 {
	char name[MAX_NAME_SIZE];
	char train_number[MAX_TRAIN_LEN];
	int seat_number; 
 }booking;
 
 // 定义一个结构体,作存储订单的列表;
 typedef struct
 {
 	booking* bookings;
 	size_t size;
 	size_t capacity;
 }booking_list;
 
 // 创建一个订单列表并进行初始化;
 booking_list* create_list(size_t initial_capacity)
 {
 	booking_list* list = (booking_list*)malloc(sizeof(booking_list));
 	if(list == NULL)
 	{
 		printf("Error:Failed to allocate memory for the booking list structure.\n");
 		return NULL;
	 }
	list->bookings = (booking*)malloc(initial_capacity * sizeof(booking));
	if(list->bookings == NULL)
	{
		printf("Error:Failed to allocate memory for the internal booking array.\n");
		free(list);
		return NULL;
	}
	list->size = 0;
	list->capacity = initial_capacity;
	return list;
 }
 
 // 向列表中添加一个新的订单;
 int append_booking(booking_list* list, const char* name, const char* train_number, int seat_number) 
 {
 	if(list->size >= list->capacity)
 	{
 		size_t new_capacity = list->capacity * 2;
 		booking* new_bookings = (booking*)realloc(list->bookings, new_capacity * sizeof(booking));
 		if(new_bookings == NULL)
 		{
 			printf("Error:Failed to reallocate memory for the interal booking array.\n");
 			return -1;
		 }
		list->bookings = new_bookings;
		list->capacity = new_capacity;
	 }
	strncpy(list->bookings[list->size].name, name, MAX_NAME_SIZE - 1);
	strncpy(list->bookings[list->size].train_number, train_number, MAX_TRAIN_LEN - 1);
	list->bookings[list->size].seat_number = seat_number;		// 座位号赋值给新的订单; 
	list->size++;		// 订单数量更新; 
	return 0; 		// 表示添加成功; 
 }
 
 // 在列表中查找一个订单;
 int find_booking(booking_list* list, const char* name, const char* train_number, int* seat_number)
 {
 	// 遍历列表;
	for(size_t i = 0; i < list->size; i++)
	{
		if(strncmp(list->bookings[i].name, name, MAX_NAME_SIZE) == 0 && strncmp(list->bookings[i].train_number, train_number, MAX_TRAIN_LEN) == 0)
		{
			*seat_number = list->bookings[i].seat_number;
			return 0;		// 比较前两个字符,如果相同返回 0; 
		}
	 } 
	return -1;		//未找到匹配项,查找失败,返回 -1; 
  } 

// 在列表中删除一个订单;
int delete_booking(booking_list* list, const char* name, const char* train_number)
{
	// 遍历列表;
	for(size_t i = 0; i < list->size; i++)
	{
		if(strncmp(list->bookings[i].name, name, MAX_NAME_SIZE) == 0 && strncmp(list->bookings[i].train_number, train_number, MAX_TRAIN_LEN) == 0)
		{
			for(size_t j = i; j < list->size - 1; j++)
			{
				list->bookings[j] = list->bookings[j + 1];
			}
			list->size--;
			return 0;
		}
	 } 
	return -1;
 } 
 
 // 打印列表中所有订单;
 void print_list(booking_list* list)
 {
 	for(size_t i = 0; i < list->size; i++)
 	{
 		printf("Name:%s, Train number:%s, Seat number:%d \n", list->bookings[i].name, list->bookings[i].train_number, list->bookings[i].seat_number);
 		
	 }
  } 
  
 // 删除列表,释放内存;
 void delete_list(booking_list* list)
 {
 	free(list->bookings);
 	list->bookings = NULL;
 	free(list);
 	// list = NULL;    可以不要这一步; 
  } 
  
 // 主函数main测试;
 int main(void)
 {
 	// 创建列表;
	booking_list* list = create_list(5);
	
	// 添加订单;
	append_booking(list, "Morandi", "G9673", 12);
	append_booking(list, "Morandi_01", "G762", 3);
	append_booking(list, "Morandi_02", "G8940", 5);
	append_booking(list, "Morandi_03", "K472", 14);
	
	// 打印订单;
	puts("========================================="); 
	puts("所有订单如下:");
	print_list(list);
	
	// 查询某个订单;
	puts("========================================="); 
	int seat_number;
	if(find_booking(list, "Morandi", "G9673", &seat_number) == 0)
	{
		printf("Name:Morandi, Train_number:G9673, Seat_number:%d.\n",seat_number);
	}
	else
	{
		printf("Booking not found.\n");
	}
	
	// 删除某个订单;
	puts("========================================="); 
	if(delete_booking(list, "Morandi_01", "G762") == 0)
	{
		printf("Booking deleted.\n");
	 } 
	else
	{
		printf("Failed to delete booking.\n");
	}
	
	// 打印修改后的所有订单;
	puts("========================================="); 
	puts("修改之后的订单如下:");
	print_list(list); 
	
	// 删除订单列表;
	puts("========================================="); 
	delete_list(list);
	 
 	return 0;
  } 

(希望鄙人代码能够帮到你,欢迎提供建议) 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值