Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Подбельский Фомин_Программирование на языке СИ_...doc
Скачиваний:
356
Добавлен:
10.08.2019
Размер:
53.81 Mб
Скачать

Печать результатов сортировки.

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

Рис. 8.20. Пример бинарного дерева

Такое дерево легко получится при вводе, например, такой последовательности букв латинского алфавита: D, В, С, F, А, Е, G (введены все буквы в диапазоне от А до G). Рассматривая это дерево, можно заметить, что буквы А и G (открывающая и закрывающая отсортированную последовательность) расположены на третьем (нижнем) уровне, соответственно слева и справа. Для того чтобы напечатать буквы в алфавитном порядке, необходимо спуститься от корня дерева по левым ветвям до самой нижней вершины. Там и будет первый элемент отсортированной последовательности букв. Увидев, что левый указатель равен NULL, напечатаем А и проверим указатель правой ветви. Он также равен NULL, поэтому поднимемся к предыдущей вершине и напечатаем В. Спускаемся от В по правой ветви к С и видим, что вниз по левой ветви от С указатель равен NULL, следовательно, печатаем С. Далее идем вверх к В (эта буква уже напечатана), поднимаемся к D и печатаем ее.

Аналогичным образом обходим правое от D поддерево. В итоге получается маршрут, изображенный на рис. 8.21.

Рис. 8.21. Обход бинарного дерева при печати

Ниже приводится текст функции print(), осуществляющей обход дерева и печать строк из вершин в алфавитном порядке. Для запоминания указателей на предыдущие узлы в функции организован стек глубиной 10 элементов в виде массива из 10 указателей на структуру типа struct node.

Оператор stack[i++] = ptr; заносит в стек значение указателя ptr и затем (постфиксная операция ++) увеличивает значение индекса (!) массива на 1. После этого stack[i] становится первым свободным элементом стека. Для выборки из стека значения указателя необходимо сначала уменьшить индекс i на единицу, с тем, чтобы stack[i] был не первым свободным элементом массива, а последним записанным:

Текст функции print( ):

Подробно процесс подготовки и выполнение программы сортировки на основе бинарного дерева в операционных системах UNIX, MS-DOS и Windows обсуждаются в главе 9.

Раздел 2. Выполнение программ в разных операционных системах Глава 9. Подготовка и выполнение программ

Упрощенная схема подготовки программ, существующая во многих операционных системах, изображена на рис. 9.1 (см. также §2.1, рис. 2.1):

Рис. 9.1. Упрощенная схема подготовки исполняемой программы

Текст программы, набранный в любом текстовом редакторе и сохраненный в файле в виде последовательности кодов символов, называется исходным модулем. Имена файлов, как правило, имеют сложную структуру: кроме собственно имени файла (произвольно назначаемого пользователем) указывается расширение, задающее тип файла. Файлы, содержащие тексты программ и функций на языке Си, обычно имеют расширение "с". В тех операционных системах , где на уровне файловой системы не различаются строчные и прописные буквы (MS-DOS или Windows), расширение может иметь и такой вид - "С". В любом случае имя файла от его расширения отделяется точкой.

Исходные модули обрабатываются компилятором, в результате чего получается промежуточный модуль, называемый объектным модулем. Объектный модуль состоит из двух основных частей: тела модуля, представляющего собой программу в кодах команд конкретной ЭВМ, и заголовка, содержащего внешние имена (имена переменных, используемых в данном модуле, но определенных в других модулях). Эта информация необходима для построения из набора объектных модулей программы или программной системы, готовой к выполнению.

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

Существование промежуточного объекта (модуля) в цепочке построения, готовой к выполнению программы, позволяет:

• сделать систему программирования гибкой и удобной в эксплуатации за счет предварительной разработки библиотек объектных модулей, содержащих множество вспомогательных функций;

• разрабатывать и использовать собственные библиотеки объектных модулей, содержащие функции, общие для отдельных частей программной системы;

• при модификации программной системы перестраивать только части, подвергшиеся доработке.

На различных этапах разработки программы или программной системы программистом может применяться разная технология сборки готовой к выполнению программы, например:

• главная функция и все подготовленные программистом вспомогательные функции программной системы находятся в одном текстовом файле;

• главная функция и вспомогательные функции находятся в разных текстовых файлах, но собираются в один исходный модуль с помощью препроцессорных команд #include;

• главная функция и вспомогательные функции находятся в разных текстовых файлах, транслируются отдельно, и исполняемая программа собирается компоновщиком из объектных модулей;

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

Пример оформления программы в нескольких текстовых файлах приведен в §8.1. Программа для исследования псевдослучайной последовательности может быть собрана в один файл с помощью директив #include. Рассмотрим вариант такого же построения программной системы с объединением всех исходных модулей в один файл с помощью препроцессорных директив #include на примере работы с базой данных о сотрудниках предприятия из §8.2.

Исходный модуль, объединяющий все функции и объекты программы, будет иметь следующую структуру:

• определения внешних переменных, т.е. объектов, используемых во вспомогательных функциях (массив структур st типа struct person, управляющие переменные);

• главная функция и вспомогательные функции:

• main( ) {.....} /* главная функция */

• init( ) {.....} /* инициализировать базу данных */

• find( ) {.....} /* поиск в списке занятых элементов */

• fr( ) {.....} /* возвратить освобожденный элемент в

список свободных элементов */

• delete( ) {.....} /* удалить сведения о сотруднике */

• input( ) {.....} /* ввод данных о новом сотруднике */

• print( ) {.....} /* печать содержимого базы данных */

• save( ) {.....} /* сохранить базу данных в файле */

• load( ) {.....} /* загрузить базу данных из файла */

Собрать в один файл все модули программы можно с помощью препроцессора, если включить в текст основной функции набор препроцессорных директив:

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

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

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

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

Рассмотрим соответствующие инструментальные средства в таких популярных операционных системах, как UNIX, MS-DOS, Windows.