Связанный список для массива фильмов

Я пытаюсь создать связанный список, в который кто-то может ввести названия некоторых фильмов и свой рейтинг. Вот что у меня есть на данный момент:

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

#define TITLE_MAX 50
void mygets(char buffer[], size_t limit);

// linked list
struct film {
    char title[TITLE_MAX];
    int rating;
    struct film *next;
};

int main(void)
{
    struct film *head = NULL, *current = NULL;
    char tbuffer[TITLE_MAX];

    while (true) 
    {
        printf("Enter the name of the film (empty line to stop)n");
        mygets(tbuffer, TITLE_MAX);
        if (*tbuffer==' ') break;

        if (!head) 
            current = head = malloc (sizeof(struct film));
        else
            // assignment associates right-to-left so can do this trick
            current = current->next = malloc (sizeof(struct film));

        current->next = NULL;
        strcpy(current->title, tbuffer);
        printf("Now enter in your rating for it: ");
        scanf("%d", &(current->rating));
        for (int c; (c=getchar()) != 'n' && c != EOF; ); // clean up garbage in scanf -- should probably write a function for this
    }

    // print the Linked List
    current = head;
    for (int i=1; current != NULL; current=current->next)
        printf("Linked List Item #%d: %s (Rating: %d)n", 
                i++, current->title, current->rating
        );

    for(struct film *current=head, *next; current != NULL; current=next) {
        next = current->next;
        free(current);
    }

}

void mygets(char buffer[], size_t limit)
{
    // first non-space char until newline or EOF, 
    // eating any extra chars in buffer
    // will add   at the end, so will get up to N-1 chars

    int c, idx=0;
    bool started=false;
    while ((c=getchar()) != EOF && c != 'n')
    {
        if (!started && isspace(c));
        if (idx < limit-1) 
            buffer[idx++] = c;
    }
    buffer[idx] = ' ';

}

Как это выглядит до сих пор? Что можно улучшить? Кроме того, в реальных реализациях связанного списка, какие дополнительные метаданные хранятся — например, такие вещи, как размер списка и тому подобное.

Кроме того, как free загляните туда. Для связанного списка имеет значение, в каком направлении я освобождаю данные?

1 ответ
1

Ошибки

  • Быстрая ошибка: if (!started && isspace(c)); не имеет никакого эффекта. Ты хотел continue; или что-то там? Как бы то ни было, вы все же сохраните ведущие пробелы в буфере, что, по-видимому, не то, что вам нужно.

    gcc -Wextra дает предупреждение, кстати.

    (Вы тестировали программу на строках ввода с ведущими пробелами? Я не думаю, что это могло бы когда-либо сработать.)

    И в настоящее время у вас нет возможности started когда-либо стать правдой …

Улучшения качества кода

  • mygets не лучший выбор для имени вашей функции. Читатель неправильно догадается, что он ведет себя как стандартный fgets (или того хуже, ранее стандартный gets), но отличается тем, что (предполагается) убирает ведущие пробелы. Я бы выбрал что-то более похожее на get_film_title. Вы можете попытаться объяснить в имени функции, что она удаляет пробелы, но это может сделать его слишком длинным.

  • Я бы разделил три задачи по заполнению списка, его распечатке и освобождению на три отдельные функции, вместо того, чтобы сбрасывать их все в main.

  • В while (true) идиома с break; заставляет читателя искать в теле цикла условие завершения. Посмотрите, сможете ли вы придумать чистый способ реструктуризации, чтобы избежать этого. Если нет, по крайней мере добавьте комментарий, чтобы объяснить, когда цикл завершится.

  • Посмотрите, сможете ли вы реструктурировать if (!head) чтобы не писать malloc вызовите дважды, что представляет опасность, если вы в конечном итоге захотите изменить его, но пропустите обновление обоих экземпляров.

  • Некоторые предложат malloc(sizeof *current) вместо sizeof(struct film). Я тоже не испытываю сильных чувств.

  • Каждый раз, когда вам нужен комментарий для объяснения «трюка», когда комментарий длиннее, чем дополнительный код, который его не нужно вводить, вы, вероятно, пытаетесь быть слишком умным. (И я нашел это сбивающим с толку даже комментарием.) Просто запишите два утверждения.

  • Рассмотрите возможность переноса задачи чтения рейтинга и «очистки входного потока» в отдельную функцию. Это неудобно, когда часть операций ввода-вывода передается подпрограмме (mygets), а некоторые остались позади.

  • for (int c; ...): А for цикл, в котором ничего делать не нужно либо из предложение init или итерация-выражение вероятно, должен быть просто while.

  • В коде печати хорошо сохранить for сосредоточен на собственном итераторе цикла, который здесь current, а не смешивать его между current и i (и поскольку последний не является итератором, он может использовать другое имя). И незачем хоронить приращение i внутри заявления, которое делает что-то еще. Я бы написал:

int film_number = 1;
for (current = head; current != NULL; current = current->next) {
    printf("...", film_number, current->title, current->rating);
    film_number++;
}
  • Почему в коде освобождения вы объявляете новую переменную с именем current, слежка за существующей?

  • В for цикл и использование переменной next в коде освобождения может быть умнее, чем читабельнее. Я бы написал проще:

current = head;
while (current) {
    struct film *tmp = current;
    current = current->next;
    free(tmp);
}
  • Я думаю, что ваш план по освобождению списка от начала до конца подходит. С односвязным списком не так-то просто сделать наоборот. (Если вы не хотите вместо этого создать список с двойной связью.

  • Проверка ошибок для каждого ввода и функции распределения памяти. Да, это скучно, но необходимо для любой реальной программы, и даже для игрушечных программ это очень полезно для отладки.

Предлагаемые улучшения

  • Что касается дополнительных метаданных: храните только то, что вам нужно. Если знание размера или последнего элемента станет полезным для вашего приложения, добавьте его. Но я не предлагаю переоценивать полностью общий и многофункциональный связанный список для конкретной программы; если вы хотите заново изобрести общий контейнер связанного списка, это отдельный проект (и тот, который может быть лучше для языка, который действительно поддерживает универсальное программирование).

  • С помощью scanf чтение пользовательского ввода не является идеальным за пределами игрушечных программ, так как затрудняет обнаружение неправильного ввода и восстановление после него. Лучше всего прочитать всю строку ввода и проанализировать ее с помощью sscanf.

  • Рассмотрите возможность использования динамического размещения, чтобы названия фильмов имели произвольную длину. В противном случае пользователь, который хочет оценить Англичанин, который поднялся на холм, но спустился с горы (57) будет разозлен.

  • Программы командной строки, которые запрашивают интерактивный ввод и заканчиваются чем-то вроде пустой строки, редко встречаются за пределами игрушечных примеров. Во-первых, их сложно адаптировать для пакетной обработки или конвейеров. Полезные программы командной строки чаще всего не запрашивают и читают ввод до тех пор, пока не встретится конец файла.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *