引言
很多人在学习数据结构的时候感觉抽象至极,其实把数据结构和编程语言分开来学可以极大减少这些抽象。前篇我们介绍了用C语言去实现一个基础的数据结构(线性表->动态数组)。本片内容,我将和大家分享一个简单而实用的车票预定系统的实现。全程非常简单,只用到了编程中的基础知识(数组、函数、结构体、指针、控制语句),为新手小白量身打造,开始上车吧。
目录
代码结构讲解
该部分提供代码的整体结构,并对各个模块进行逐步解释,通俗易懂。(代码实现方式有很多种,此次完整代码在最后,鼓励创新)/(代码不算高级,应对学校作业绰绰有余)
结构体定义: booking
和 booking_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_bookings
为NULL
,表示内存分配失败,打印错误信息并返回-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].name
、list->bookings[i].train_number
和 list->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;
}
(希望鄙人代码能够帮到你,欢迎提供建议)