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

8.2. Структуры и обработка списков в основной памяти Постановка задачи.

Постановка задачи. Рассмотрим задачу, в которой весьма целесообразен структурный тип данных. Предположим, что в памяти ЭВМ необходимо хранить сведения о сотрудниках некоторого учреждения и иметь возможность выдавать справки по личному составу, а также корректировать сохраняемые данные при изменениях сведений. Таким образом, нужна несложная база данных о сотрудниках. Для каждого сотрудника должно быть указано (в скобках определен тип переменной):

• фамилия (char[ ]);

• название отдела (char[ ]);

• номер отдела (int);

• оклад (int);

• код должности (int);

• название должности (char[ ]);

• дата поступления на работу (char[ ]).

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

• ввод сведений о новом сотруднике;

• удаление сведений о сотруднике;

• вывод на экран дисплея текущего состояния базы данных с ранжированием информации о сотрудниках по одному из следующих признаков:

- по окладам;

- по отделам;

- по алфавиту (по фамилиям);

- по датам поступления на работу.

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

Напомним, что приведенное описание не определяет структуру с именем person. Вводится только "формат" входящих в структуру данных, и этот формат как структурный тип обозначается именем person.

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

Это определение отводит память для 100 индексированных переменных с общим именем st, каждая из которых является структурой типа person. Заметим, что, как и все массивы, массивы структур индексируются, начиная с 0.

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

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

где point - указатель на структуру типа person.

Описав указатель на структуру, можно присвоить ему адрес конкретной структуры того же типа: point = &st[3];. После такого определения значения указателя появляется еще одна возможность доступа к элементам структуры. Ее обеспечивает операция '->', позволяющая обратиться к любому элементу структуры, с которой в данный момент связан указатель. Например, к тому же элементу st[3].price можно теперь обратиться, используя конструкцию point->price.

Напомним возможность применения указателя в уточненном имени элемента структуры. Операция раскрытия ссылки позволяет обратиться к объекту, который адресует указатель, в частном случае - на структуру. Таким образом, выражение (*point).price (обратите внимание на необходимость скобок, так как операция раскрытия ссылки '*' относится только к указателю point и ее приоритет ниже, чем у операции '.') эквивалентно point->price и st[3].price.

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

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

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

Рис. 8.3. Пример двунаправленного связного списка из трех элементов в основной памяти

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

• на предыдущий элемент списка (prior);

• на следующий элемент списка (next).

Полное описание структурного типа person будет иметь вид:

Последние две строки (см. рис. 8.3) определяют указатели (prior и next) на структуры типа person, т.е. в качестве элементов структуры используются указатели на структуру того же типа. Так как список сотрудников располагается в основной памяти в массиве, содержащем фиксированное количество (100) экземпляров структур типа person, а функции динамического распределения памяти не используются, примем следующие технические решения:

• при подготовке массива структур (т.е. при инициализации базы данных) все элементы массива объединяются в список свободных элементов;

• список занятых элементов вначале пуст;

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

Замечание. Вводимый список сотрудников должен быть упорядочен по алфавиту;

• при удалении сведений о сотруднике из базы данных освободившийся элемент удаляется из списка занятых элементов и возвращается в список свободных элементов.

На рис. 8.4 изображен в рабочем состоянии массив структур, содержащий сведения о сотрудниках. Указатели в первых и последних элементах списков, не указывающие соответственно на предыдущий и следующий элементы, получают значение, равное NULL.

Рис. 8.4. Рабочее состояние массива структур (в списке свободных элементов сейчас только 2 элемента)

Схема, приведенная на рис. 8.5, поясняет процесс удаления элемента из списка занятых элементов. Был удален элемент под номером 2. Физически он остается на месте, но входит после удаления в другой список (свободных элементов), так как указатели на предыдущий и последующий элементы получили другие значения.

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

main( ) - главная функция;

init( ) - инициализация (подготовка) базы данных;

input( ) - ввод сведений о новом сотруднике;

delete( ) - удаление сведений о сотруднике;

print( ) - распечатка базы данных;

save( ) - запись базы данных в файл;

load( ) - загрузка базы данных из файла.

Рис. 8.5. Состояние массива структур после удаления элемента 2 из списка занятых элементов