Один из прототипов в заголовочном файле несколько сложнее остальных:
/* операция: применение функции к каждому элементу списка */ /* предусловия: plist указывает на инициализированный список */ /* pfun указывает на функцию, которая принимает */ /* аргумент Item и не имеет возвращаемого значения */ /* постусловия: функция, указанная pfun, выполняется один */ /* раз для каждого элемента в списке */ void Traverse (const List *plist, void (* pfun)(Item item) );
Аргумент pfun представляет собой указатель на функцию. В этом случае он является указателем на функцию, которая принимает значение item в качестве аргумента и не имеет возвращаемого значения. Возможно, вы помните из главы 14, что указатель на функцию можно передавать в виде аргумента другой функции, которая сможет вызывать эту указанную функцию. Так, например, pfun может указывать на функцию, отображающую элемент. Функция Traverse() будет применять эту функцию к каждому элементу списка, в результате отображая весь список.
Использование интерфейса
Мы заявляем, что этот интерфейс можно использовать для написания программы, не располагая никакими дополнительными деталями — например, ничего не зная о том, как реализованы функции интерфейса. Давайте прямо сейчас напишем новую версию программы вывода информации о фильмах еще до создания вспомогательных функций. Поскольку интерфейс определен в терминах типов List и Item, программа должна быть создана с применением этих же типов. Ниже показан один из возможных планов, представленный с помощью псевдокода:
736 глава 17
Создать переменную List.
Создать переменную Item.
Инициализировать список пустым содержимым.
Пока список не заполнен и есть входные данные:
Прочитать входные данные и поместить их в переменную Item.
Добавить элемент в конец списка.
Посетить каждый элемент списка и отобразить его.
Программа, приведенная в листинге 17.4, следует этому базовому плану; кроме того, в нее добавлен код для проверки ошибок. Взгляните, как в ней используется интерфейс, описанный в файле list.h (листинг 17.3). Обратите также внимание, что листинг содержит код функции showmovies(), которая соответствует прототипу, требуемому функцией Traverse(). Поэтому программа может передавать указатель showmovies в функцию Traverse(), чтобы та могла применять функцию showmovies() к каждому элементу списка. (Вспомните, что имя функции является указателем на эту функцию.)
Листинг 17.4. Программа films3.c

Расширенное представление данных 737

Реализация интерфейса
Разумеется, еще предстоит реализовать интерфейс List. Подход, принятый в С, предусматривает сбор определений функций в файле по имени list.с. Тогда полная программа будет состоять из трех файлов: list.h, в котором определены структуры данных и предоставлены прототипы для пользовательского интерфейса, list.с, содержащего код функций для реализации интерфейса, и films3.c, представляющего собой файл исходного кода, где интерфейс списка применяется для решения конкретной задачи. Одна из возможных реализаций файла list.с показана в листинге 17.5. Чтобы запустить программу, необходимо скомпилировать оба файла films3.c и list .с и скомпоновать их. (Компиляция многофайловых программ обсуждалась в главе 9). Вместе файлы list.с, list.с и films3.c образуют завершенную программу (рис. 17.5).
Листинг 17.5. Файл реализации list.с

738 Глава 17

Расширенное представление данных 739

Замечания по поводу программы
С файлом list.с связано много интересных особенностей. Скажем, он иллюстрирует ситуацию, когда можно использовать функции с внутренним связыванием. Как описано в главе 12, функции с внутренним связыванием известны только в файле, где они определены. При реализации интерфейса иногда удобно применять вспомогательные функции, которые не являются частью официального интерфейса. Например, в приведенной программе функция CopyToNode() используется для копирования значения типа Item в переменную типа Item. Поскольку эта функция — часть реализации, но не интерфейса, с помощью квалификатора класса хранения static мы скрыли ее в файле list.с. А теперь давайте проанализируем остальные функции.
Читать дальше