\begingroup

请帮助重构此代码。我是 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

\begingroup

立即记录:

  • 尽量避免使用全局变量。你可以将保留数组传递给每个函数,这样调试起来更容易,代码也更灵活。
  • 避免使用魔法数字。您已经这样做了,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


    – 


\begingroup

涵盖一些尚未充分探讨的要点:

用户输入

像这样的代码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)将调用代码复制ReservationnewReservation。考虑传递结构的地址。

// void insertReservation(Reservation newReservation)
void insertReservation(const Reservation *newReservation)

然后后来:

// reservations[i + 1] = newReservation
reservations[i + 1] = *newReservation

输入验证

日期输入有误。请考虑验证日期,以免预订 2024 年 9 月 31 日。

甚至公司的名称也值得认可。

\endgroup

0

\begingroup

不要使用旧的// int使用布尔类型,这是每种现代语言的默认类型。https 01

#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


    –