if (head == NULL) /* первая структура */
head = current;
else /* последующие структуры */
prev->next = current;
В этом коде prev — указатель на структуру, выделенную в прошлый раз.
Далее понадобится установить члены структуры в соответствующие значения. В частности, член next должен быть установлен в NULL для указания на то, что текущая структура является последней в списке. Вы должны скопировать название фильма из массива input в член title и получить значение для члена rating. Эти действия выполняет следующий код:
728 Глава 17
current->next = NULL;
strcpy(current->title, input);
puts("Введите свое значение рейтинга <0 — 10>:");
scanf("%d", ¤t->rating);
Поскольку вызов s_gets() ограничивает вводимые данные пределом в TSIZE - 1 символов, строка в массиве input поместится в член title, поэтому вполне безопасно применять функцию strcpy().
Наконец, вы должны подготовить программу к следующему циклу ввода. В частности, указатель prev необходимо установить так, чтобы он ссылался на текущую структуру, т.к. после ввода названия следующего фильма и распределения следующей структуры текущая структура станет предыдущей. Программа устанавливает этот указатель в конце цикла:
prev = current;
Работает ли программа? ниже показаны результаты пробного запуска.
Введите название первого фильма:
Spirited Away
Введите свое значение рейтинга <0-10>:
9
Введите название следующего фильма (или пустую строку для прекращения ввода): The Duelists
Введите свое значение рейтинга <0 — 10>:
8
Введите название следующего фильма (или пустую строку для прекращения ввода): Devil Dog: The Mound of Hound
Введите свое значение рейтинга <0-10>:
1
Введите название следующего фильма (или пустую строку для прекращения ввода) : Список фильмов:
Фильм: Spirited Away Рейтинг: 9
Фильм: The Duelists Рейтинг: 8
Фильм: Devil Dog: The Mound of Hound Рейтинг: 1
Программа завершена .
Освобождение памяти, занимаемой списком
Во многих средах программа освободит память, выделенную с помощью функции malloc(), при своем завершении, но лучше, чтобы в привычку вошло уравновешивание каждого вызова malloc() вызовом free(). Таким образом, программа очищает используемую память с применением функции free() к каждой выделенной структуре:
current = head; while (current != NULL)
{
free(current); current = current->next;
}
дополнительные соображения
Возможности программы films2 .с несколько ограничены. Например, в ней отсутствует проверка, удалось ли функции malloc() найти запрошенную память, и она лишена каких-либо средств для удаления элементов из списка. Тем не менее, такие упущения могут быть устранены. Скажем, можно добавить код, который проверяет, является ли возвращаемое значение malloc() равным NULL (признак неудачи в полу-
Расширенное представление данных 729
чении желаемой памяти). Если программа нуждается в удалении записей, в ней можно предусмотреть дополнительный код.
Такой специализированный подход к решению проблем и добавлению функциональных возможностей по мере необходимости не всегда является наилучщим стилем программирования. С другой стороны, обычно не удается предугадать абсолютно все, что потребуется программе. По мере роста масштаба проектов модель заблаговременного планирования всех необходимых функциональных средств становится все менее реалистичной. Было замечено, что самыми успешными оказывались те крупные программы, которые поэтапно развивались от небольших удачных программ.
Учитывая, что планы могут пересматриваться, имеет смысл разрабатывать первоначальные идеи в манере, упрощающей модификацию. Пример в листинге 17.2 не следует этому принципу. Так, в нем проявляется тенденция к смешиванию деталей кодирования и концептуальной модели. Например, в этом коде концептуальная модель заключается в том, что элементы добавляются в список. Программа затеняет этот интерфейс, вынося на передний план такие детали, как malloc() и указатель current->next. Весьма желательно, если бы вы смогли писать программу в стиле, который делает очевидным то, что вы добавляете элемент в список, и одновременно скрывает такие вспомогательные действия, как вызов функций управления памятью и установка указателей. Отделение пользовательского интерфейса от деталей реализации упростит понимание и обновление программы. Уиомянугых целей можно достичь, создав программу заново. Начав разработку с нуля, вы можете достичь таких целей. Давайте посмотрим как.
Абстрактные типы данных
В программировании вы пытаетесь сопоставить необходимый тип данных с нуждами программной задачи. Например, для представления количества имеющихся пар обуви можно было бы использовать тип int, а для представления средней цены одной пары — тип float или double. В приведенных примерах программ, связанных с фильмами, данные формировали список элементов, каждый из которых состоял из названия фильма (строки С) и значения рейтинга (типа int). Ни один из базовых типов С не соответствует этому описанию, поэтому для представления отдельных элементов мы определили структуру, а затем создали пару методов для объединения последовательности структур в список. В сущности, мы применили возможности языка С по разработке нового типа данных, удовлетворяющего конкретным потребностям, но делали это бессистемно. Теперь мы примем систематичный подход к определению типов.
Читать дальше