int size; /* количество записей в списке */
} List; /* альтернативное определение списка */
Можно было бы добавить второй указатель, предназначенный для отслеживания конца списка. Позже вы увидите соответствующий пример. Пока давайте ограничимся первым определением типа List. Важно помнить, что объявление
List movies;
следует рассматривать как определение списка, а не установку указателя на узел или структуры.
Расширенное представление данных 733
Точное представление данных списка movies является деталью реализации, которая не должна быть видна на уровне интерфейса.
Например, при запуске программа должна инициализировать указатель на заголовок значением NULL, но не следует применять код вроде такого:
movies = NULL;
А почему? По той причине, что впоследствии может оказаться, что реализация типа List в виде структуры подходит больше, и тогда потребуется следующая инициализация:
movies.next = NULL; movies.size = 0;
Никто из тех, кто использует тип List, не должен беспокоиться о подобных нюансах. Вместо этого должна быть возможность записывать приблизительно такой код:
InitializeList(movies);
Программистам требуется знать только о том, что для инициализации списка они должны применять функцию InitializeList(). Они не обязаны знать точную реализацию данных для переменной List. Это является примером сокрытия данных — искусства маскировки подробностей представления данных от более высоких уровней программирования.
Для предоставления руководства пользователю прототип функции можно сопровождать следующими строками:
/* операция: инициализация списка */
/* предусловия: plist указывает на список list */
/* постусловия: список инициализирован пустым содержимым */
void InitializeList(List * plist);
Есть три момента, на которые вы должны обратить внимание. Во-первых, комментарии описывают предусловия, т.е. условия, которые должны быть удовлетворены до вызова функции. Например, здесь необходим список, предназначенный для инициализации. Во-вторых, комментарии описывают постусловия — условия, которые должны быть удовлетворены после выполнения функции. Наконец, в-третьих, в качестве своего аргумента функция использует указатель на список, а не сам список, поэтому вызов функции будет иметь такой вид:
InitializeList(smovies);
Причина заключается в том, что в С аргументы передаются по значению. Таким образом, единственный способ позволить функции С изменять значения из вызывающей программы предусматривает применение указателя на эту переменную. Как видите, здесь ограничения языка приводят к некоторому отличию интерфейса от его абстрактного описания.
Принятый в языке С метод объединения информации о типе и функциях в единый пакет предполагает помещение определений для типа и прототипов функций (в том числе комментариев с пред- и постусловиями) в заголовочный файл. Этот файл должен предоставлять всю информацию, в которой нуждается программист для использования типа. Заголовочный файл для простого типа list показан в листинге 17.3. В нем конкретная структура определена как относящаяся к типу Item, после чего тип Node определен в терминах Item и тип List — в терминах Node. Затем в функциях, представляющих операции над списком, типы Item и List применяются для аргументов. Если функции необходимо модифицировать аргумент, она использует указатель
734 Глава 17 на соответствующий тип, а не сам тип напрямую. В файле имена функций начинаются с прописных букв для их обозначения как части интерфейсного пакета. Кроме того, для защиты от множественного включения файла применяется прием с #ifndef, который обсуждался в главе 16. Если ваш компилятор не поддерживает тип bool из стандарта С99, можете заменить в заголовочном файле строку
#include /* функциональная возможность С99 */
такой строкой:

Листинг 17.3. Заголовочный файл для интерфейса list.li
Расширенное представление данных 735

Список модифицируют только функции InitializeList() , Addltem() и EmptyTheList, поэтому формально только они требуют аргумента типа указателя. Однако если бы пользователю пришлось помнить о необходимости передачи аргумента List одним функциям и его адреса другим, то это могло бы приводить к путанице. Таким образом, для упрощения задачи пользователя во всех функциях используются аргументы типа указателей.
Читать дальше