请帮助重构此代码。我是 C 语言初学者,想学习干净的代码和最佳实践。这是一个具有以下功能的应用程序:
- 用户可以进行预订(年、月、日、公司名称)
- 预订信息存储在一个结构体中
- 如果日期不可用,则会通知用户
- 使用自定义排序机制更新预订结构
#include <stdio.h>
#include <string.h>
#define MAX_RESERVATIONS 100
// Structure for a reservation
typedef struct
{
int year;
int month;
int day;
char companyName[50];
} Reservation;
// Global variables
Reservation reservations[MAX_RESERVATIONS];
int reservationCount = 0;
// Function declarations
void showMenu();
void handleInput(int option);
void addReservation();
void inputReservation(Reservation *newReservation);
int isDateAvailable(int year, int month, int day);
void insertReservation(Reservation newReservation);
void printReservations();
// Main function - streamlined and clean
int main()
{
int option;
while (1)
{
showMenu();
if (scanf("%d", &option) != 1 || option == 3)
{
printf("Exiting program.\n");
break; // Exit the program
}
handleInput(option); // Delegate responsibility to handle user input
}
return 0;
}
// Show menu options
void showMenu()
{
printf("\nMenu:\n");
printf("1. New Reservation\n");
printf("2. Print Reservations\n");
printf("3. Exit\n");
printf("Choose an option: ");
}
// Handle user input
void handleInput(int option)
{
if (option == 1)
{
addReservation();
}
else if (option == 2)
{
printReservations();
}
else
{
printf("Invalid choice. Please try again.\n");
}
}
// Add a new reservation (modular: only handles adding)
void addReservation()
{
Reservation newReservation;
// Input reservation details
inputReservation(&newReservation);
// Check if the date is available
if (!isDateAvailable(newReservation.year, newReservation.month, newReservation.day))
{
printf("The room is already booked on that date.\n");
return;
}
// Insert the reservation
insertReservation(newReservation);
printf("Reservation added successfully.\n");
}
// Input reservation details (year, month, day, company name)
void inputReservation(Reservation *newReservation)
{
// Input: Date
printf("Enter the reservation date (YYYY MM DD): ");
scanf("%d %d %d", &newReservation->year, &newReservation->month, &newReservation->day);
// Input: Company name
printf("Enter the company name: ");
scanf("%s", newReservation->companyName);
}
// Check if the date is available for booking
int isDateAvailable(int year, int month, int day)
{
for (int i = 0; i < reservationCount; i++)
{
if (reservations[i].year == year &&
reservations[i].month == month &&
reservations[i].day == day)
{
return 0; // Date is not available
}
}
return 1; // Date is available
}
// Insert the reservation in the correct position (sorted by date)
void insertReservation(Reservation newReservation)
{
int i = reservationCount - 1;
// Find the correct position and shift the elements back
while (i >= 0 && (reservations[i].year > newReservation.year ||
(reservations[i].year == newReservation.year && reservations[i].month > newReservation.month) ||
(reservations[i].year == newReservation.year && reservations[i].month == newReservation.month && reservations[i].day > newReservation.day)))
{
reservations[i + 1] = reservations[i];
i--;
}
// Insert the new reservation
reservations[i + 1] = newReservation;
reservationCount++;
}
// Print all reservations
void printReservations()
{
if (reservationCount == 0)
{
printf("No reservations found.\n");
return;
}
printf("Current reservations:\n");
for (int i = 0; i < reservationCount; i++)
{
printf("%d-d-d: %s\n", reservations[i].year, reservations[i].month, reservations[i].day, reservations[i].companyName);
}
}
\endgroup
最佳答案
3
立即记录:
- 尽量避免使用全局变量。你可以将保留数组传递给每个函数,这样调试起来更容易,代码也更灵活。
- 避免使用魔法数字。您已经这样做了,
MAX_RESERVATIONS
但没有在您的结构中设置公司名称的最大长度。
我还建议您创建一个结构来表示日期,并将其Date
作为预订结构的组成部分。例如
typedef struct Date {
int year;
int month;
int day;
} Date;
您现在可以定义日期的比较函数。例如
int date_cmp(const void *a, const void *b) {
Date c = *(Date *)a;
Date d = *(Date *)b;
if (c.year > d.year) return 1;
else if (c.year < d.year) return -1;
else if (c.month > d.month) return 1;
else if (c.month < d.month) return -1;
else if (c.day > d.day) return 1;
else if (c.day < d.day) return -1;
else return 0;
}
这将简化按日期对预订进行排序的过程,因为您现在可以更轻松地利用标准库工具,例如。
您还需要清理输入。现在,当您调用scanf
(except in main
)时,您既不会检查返回代码以确保它成功读取了您期望的数据,也不会检查读取的值是否在合理的范围内。永远不要相信您的用户。
您可能还希望定义一个ReservationSet
结构,它可以跟踪您的数组以及数组中有多少元素。这可以帮助您不必单独传递预订数量。
\endgroup
1
-
\begingroup
对于date_cmp()
,我会改用const Date *c = (const Date *)a;
,但Date
由于 较小,所以问题不大。另一种选择是const Date *c = (const Date *)a; long cv = c->year*12L*31 + c->month*31 + c->day; ... return (cv > dv) - (cv < dv);
或类似的。
\endgroup
–
|
涵盖一些尚未充分探讨的要点:
用户输入
像这样的代码scanf("%s", newReservation->companyName);
至少有 4 个问题。
-
没有宽度限制,因此目标缓冲区可能会溢出。
-
公司名称中通常带有空格,并且
"%s"
不支持这一点。 -
"%s"
首先消耗可选的前导空格,这可能导致'\n'
OP 的行输入错误。 -
scanf()
代码无法在此处以及其他调用中检查返回值是否成功scanf()
。
最好研究fgets()
将一行用户输入读入字符串,然后解析该字符串。
最大预留数量尚未测试。
reservations[i + 1] = newReservation;
可能会溢出。
有时评论只是噪音
这里的注释是多余的。代码已经使用了良好的对象和函数名称。
// Insert the reservation
insertReservation(newReservation);
printf("Reservation added successfully.\n");
最好声明void
void showMenu();
将匹配类似的调用showMenu(1,2,3);
,因为没有参数检查。
最好声明为void showMenu(void);
。
来说,这个问题可能已经不存在了。
完成后冲洗
用户输出,尤其是缺少最终的输出'\n'
,值得跟踪fflush(stdout)
以确保输出在输入之前发生。
避免复制大型结构
void insertReservation(Reservation newReservation)
将调用代码复制Reservation
到newReservation
。考虑传递结构的地址。
// void insertReservation(Reservation newReservation)
void insertReservation(const Reservation *newReservation)
然后后来:
// reservations[i + 1] = newReservation
reservations[i + 1] = *newReservation
输入验证
日期输入有误。请考虑验证日期,以免预订 2024 年 9 月 31 日。
甚至公司的名称也值得认可。
\endgroup
0
|
不要使用旧的// int
。使用布尔类型,这是每种现代语言的默认类型。https 0
1
#include <stdbool.h>
int main()
{
...
while (true)
...
}
bool isDateAvailable(int year, int month, int day)
{
for (...)
{
if (...)
{
return false; // Date is not available
}
}
return true; // Date is available
}
\endgroup
2
-
\begingroup
谢谢您的回答 Nayuki!很高兴看到您展示的抽象结构。我们是否使用return true;
in theint main(){ return false; };
而不是return 0
?
\endgroup
– -
\begingroup
的返回值main()
比较特殊,因为它遵循 的规则。您实际上应该返回EXIT_SUCCESS
而不是 0。它不是一个bool
值。
\endgroup
–
|
|