Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Программирование на языке высокого уровня Паскаль. Учебное пособие
.pdf
Рис. 5.2. Выделение и заполнение динамической памяти
Динамические переменные можно использовать в операциях,
допустимых для величин соответствующего типа, например:
inc(p1^); p2^ := p1^ + p3^.d;
with p3^ do writeln (d, s);
ВНИМАНИЕ При присваивании указателю другого значения старое
значение теряется (рис. 5.3). Это приводит к появлению так
называемого мусора (на рисунке обозначен овалом), когда доступа к
участку динамической памяти нет, а сам он помечен как занятый.
Рис. 5.3. Мусор
Для освобождения динамической памяти используются процедуры
Dispose и Freemem, причем если память выделялась с помощью
new, следует применять Dispose, в противном случае — Freemem.
Процедура Dispose (var p : pointer) освобождает
участок памяти, выделенный для размещения динамической
переменной процедурой или функцией New, и значение указателя
p становится неопределенным.
Процедура Freemem (var p : pointer; size :
Программирование на языке высокого уровня ПаскальТ.А. Павловская
101

word) освобождает участок памяти размером size, начиная с
адреса, находящегося в p. Значение указателя становится
неопределенным.
При завершении программы используемая ею динамическая память
освобождается автоматически, поэтому явным образом освобождать
ненужную память необходимо только в том случае, если она может
потребоваться при дальнейшем выполнении программы.
Динамические структуры данных
Если до начала работы с данными невозможно определить, сколько
памяти потребуется для их хранения, память следует распределять во
время выполнения программы по мере необходимости отдельными
блоками. Блоки связываются друг с другом с помощью указателей. Такой
способ организации данных называется динамической структурой
данных, поскольку она размещается в динамической памяти и ее размер
изменяется во время выполнения программы.
Из динамических структур в программах чаще всего используются
линейные списки, стеки, очереди и бинарные деревья. Они
различаются способами связи отдельных элементов и допустимыми
операциями. Динамическая структура, в отличие от массива или
записи, может занимать несмежные участки оперативной памяти.
Динамические структуры широко применяют и для более эффективной
работы с данными, размер которых известен, особенно для решения
задач сортировки, поскольку упорядочивание динамических структур не
требует перестановки элементов, а сводится к изменению указателей на
эти элементы. Например, если в процессе выполнения программы
требуется многократно упорядочивать большой массив данных, имеет
смысл организовать его в виде линейного списка. При решении задач
поиска элемента в тех случаях, когда важна скорость, данные лучше
всего представить в виде бинарного дерева.
Элемент динамической структуры состоит из двух частей:
информационной, ради хранения которой и создается структура, и
указателей, обеспечивающих связь элементов друг с другом. Элемент
описывается в виде записи, например:
Программирование на языке высокого уровня ПаскальТ.А. Павловская
102

type
pnode = ^node;
node = record
d : word; { информационная }
s : string; { часть }
p : pnode; { указатель на следующий элемент }
end;
ПРИМЕЧАНИЕ Обратите внимание, что тип указателя pnode на
запись node определен раньше, чем сама запись. Это не противоречит
принципу "использование только после описания", поскольку для
описания переменной типа pnode информации вполне достаточно.
Рассмотрим принципы работы с основными динамическими
структурами.
Стеки
Стек является простейшей динамической структурой. Добавление
элементов в стек и выборка из него выполняются из одного конца,
называемого вершиной стека. Другие операции со стеком не
определены. При выборке элемент исключается из стека.
Говорят, что стек реализует принцип обслуживания LIFO (last in — first
out, последним пришел — первым обслужен). Стеки широко
применяются в системном программном обеспечении, компиляторах, в
различных рекурсивных алгоритмах.
Для работы со стеком используются две статические переменные:
указатель на вершину стека и вспомогательный указатель.
var top, p : pnode;
Тип указателей должен соответствовать типу элементов стека.
В пример 5.1 приведена программа, которая формирует стек из пяти
целых чисел и их текстового представления и выводит его на экран.
Функция занесения в стек по традиции называется push, а функция
Программирование на языке высокого уровня ПаскальТ.А. Павловская
103

выборки — pop.
program stack;
const n = 5;
type pnode = ^node;
node = record { элемент стека }
d : word;
s : string;
p : pnode;
end;
var top : pnode; { указатель на вершину стека }
i : word;
s : string;
const text : array [1 .. n] of string = ('one', 'two', 'three', 'four', 'five');
{ ------------------------------ занесение в стек --------------------------- }
function push(top : pnode; d : word; const s : string) : pnode;
var p : pnode;
begin
new(p);
p^.d := d; p^.s := s; p^.p := top;
push := p;
end;
{ ------------------------------ выборка из стека --------------------------- }
function pop(top : pnode; var d : word; var s : string) : pnode;
var p : pnode;
begin
d := top^.d; s := top^.s;
pop := top^.p;
dispose(top);
end;
{ ------------------------------- главная программа ----------------------------- }
begin
top := nil;
for i := 1 to n do top := push(top, i, text[i]); { занесение в стек: }
while top <> nil do begin { выборка из стека: }
top := pop(top, i, s); writeln(i:2, s);
end;
end.
Программирование на языке высокого уровня ПаскальТ.А. Павловская
104

Листинг 5.1. Использование стека
Очереди
Очередь — это динамическая структура данных, добавление элементов
в которую выполняется в один конец, а выборка — из другого конца.
Другие операции с очередью не определены. При выборке элемент
исключается из очереди. Говорят, что очередь реализует принцип
обслуживания FIFO (first in — first out, первым пришел — первым
обслужен). В программировании очереди применяются очень широко
— например, при моделировании, буферизованном вводе-выводе или
диспетчеризации задач в операционной системе.
Для работы с очередью используются указатели на ее начало и конец, а
также вспомогательный указатель:
var beg, fin, p : pnode;
Тип указателей должен соответствовать типу элементов, из которых
состоит очередь.
В пример 5.2 приведена программа, которая формирует очередь из пяти
целых чисел и их текстового представления и выводит еe на экран. Для
разнообразия операции с очередью оформлены в виде процедур.
Процедура начального формирования называется first, помещения в
конец очереди — add, а выборки — get.
program queue;
const n = 5;
type pnode = ^node;
node = record { элемент очереди }
d : word;
s : string;
p : pnode;
end;
var beg, fin : pnode; { указатели на начало и конец очереди }
i : word;
s : string;
Программирование на языке высокого уровня ПаскальТ.А. Павловская
105

const text : array [1 .. n] of string = ('one', 'two', 'three', 'four', 'five');
{ ------------------ начальное формирование очереди --------------------------- }
procedure first(var beg, fin : pnode; d : word; const s : string);
begin
new(beg);
beg^.d := d; beg^.s := s; beg^.p := nil;
fin := beg;
end;
{ --------------------- добавление элемента в конец --------------------------- }
procedure add(var fin : pnode; d : word; const s : string);
var p : pnode;
begin
new(p);
p^.d := d; p^.s := s; p^.p := nil;
fin^.p := p;
fin := p;
end;
{ ---------------------- выборка элемента из начала --------------------------- }
procedure get(var beg : pnode; var d : word; var s : string);
var p : pnode;
begin
d := beg^.d; s := beg^.s;
p := beg; beg := beg^.p;
dispose(p);
end;
{ ------------------------------- главная программа --------------------------- }
begin
{ занесение в очередь: }
first(beg, fin, 1, text[1]);
for i := 2 to 5 do add(fin, i, text[i]);
{ выборка из очереди: }
while beg <> nil do begin
get(beg, i, s);
writeln(i:2, s);
end;
end.
Листинг 5.2. Использование очереди
Программирование на языке высокого уровня ПаскальТ.А. Павловская
106

Линейные списки
В линейном списке каждый элемент связан со следующим и, возможно,
с предыдущим. В первом случае список называется односвязным, во
втором — двусвязным. Также применяются термины
"однонаправленный" и "двунаправленный". Если последний элемент
связать указателем с первым, получится кольцевой список.
Каждый элемент списка содержит ключ, идентифицирующий этот
элемент. Ключ обычно бывает либо целым числом, либо строкой и
является частью поля данных. В качестве ключа в процессе работы со
списком могут выступать разные части поля данных. Например, если
создается линейный список из записей, содержащих фамилию, год
рождения, стаж работы и пол, любая часть записи может выступать в
качестве ключа. При упорядочивании списка по алфавиту ключом будет
фамилия, а при поиске, например, ветеранов труда — стаж. Ключи
разных элементов списка могут совпадать.
Над списками можно выполнять следующие операции:
1. начальное формирование списка (создание первого элемента);
2. добавление элемента в конец списка;
3. чтение элемента с заданным ключом;
4. вставка элемента в заданное место списка (до или после элемента
с заданным ключом);
5. удаление элемента с заданным ключом;
6. упорядочивание списка по ключу.
Стек и очередь представляют собой частные случаи линейного списка с
ограниченным набором допустимых операций.
При чтении элемент списка не удаляется, в отличие от элемента,
выбираемого из стека и очереди. Для работы со списком в программе
требуется определить указатель на его начало. Чтобы упростить
добавление новых элементов в конец списка, можно также завести
указатель на конец списка.
Рассмотрим программу (пример 5.3), которая формирует односвязный
Программирование на языке высокого уровня ПаскальТ.А. Павловская
107

список из пяти элементов, содержащих число и его текстовое
представление, а затем выполняет вставку и удаление заданного
элемента. В качестве ключа используется число.
program linked_list;
const n = 5;
type pnode = ^node;
node = record { элемент списка }
d : word;
s : string;
p : pnode;
end;
var beg : pnode; { указатель на начало списка }
i, key : word;
s : string;
option : word;
const text: array [1 .. n] of string = ('one', 'two', 'three', 'four', 'five');
{ -------------- добавление элемента в конец списка --------------------------- }
procedure add(var beg : pnode; d : word; const s : string);
var p : pnode; { указатель на создаваемый элемент }
t : pnode; { указатель для просмотра списка }
begin
new(p); { создание элемента }
p^.d := d; p^.s := s; { заполнение элемента }
p^.p := nil;
if beg = nil then beg := p { список был пуст }
else begin { список не пуст }
t := beg;
while t^.p <> nil do { проход по списку до конца }
t := t^.p;
t^.p := p; { привязка нового элемента к последнему }
end
end;
{ ------------------------- поиск элемента по ключу --------------------------- }
function find(beg : pnode; key : word; var p, pp : pnode) : boolean;
begin
p := beg;
while p <> nil do begin { 1 }
if p^.d = key then begin { 2 }
Программирование на языке высокого уровня ПаскальТ.А. Павловская
108

find := true; exit end;
pp := p; { 3 }
p := p^.p; { 4 }
end;
find := false;
end;
{ -------------------------------- вставка элемента --------------------------- }
procedure insert(beg : pnode; key, d : word; const s : string);
var p : pnode; { указатель на создаваемый элемент }
pkey : pnode; { указатель на искомый элемент }
pp : pnode; { указатель на предыдущий элемент }
begin
if not find(beg, key, pkey, pp) then begin
writeln(' вставка не выполнена'); exit; end;
new(p); { 1 }
p^.d := d; p^.s := s; { 2 }
p^.p := pkey^.p; { 3 }
pkey^.p := p; { 4 }
end;
{ ------------------------------- удаление элемента --------------------------- }
procedure del(var beg : pnode; key : word);
var p : pnode; { указатель на удаляемый элемент }
pp : pnode; { указатель на предыдущий элемент }
begin
if not find(beg, key, p, pp) then begin
writeln(' удаление не выполнено'); exit; end;
if p = beg then beg := beg^.p { удаление первого элемента }
else pp^.p := p^.p;
dispose(p);
end;
{ ------------------------------------ вывод списка --------------------------- }
procedure print(beg : pnode);
var p : pnode; { указатель для просмотра списка }
begin
p := beg;
while p <> nil do begin { цикл по списку }
writeln(p^.d:3, p^.s); { вывод элемента }
p := p^.p { переход к следующему элементу списка }
Программирование на языке высокого уровня ПаскальТ.А. Павловская
109

end;
end;
{ ------------------------------- главная программа --------------------------- }
begin
for i := 1 to 5 do add(beg, i, text[i]);
while true do begin
writeln('1 - вставка, 2 - удаление, 3 - вывод, 4 - выход');
readln(option);
case option of
1: begin { вставка }
writeln('Ключ для вставки?');
readln(key);
writeln('Вставляемый элемент?');
readln(i); readln(s);
insert(beg, key, i, s);
end;
2: begin { удаление }
writeln('Ключ для удаления?');
readln(key);
del(beg, key);
end;
3: begin { вывод }
writeln('Вывод списка:');
print(beg);
end;
4: exit; { выход }
end
writeln;
end
end.
Листинг 5.3. Использование списка
Функция поиска элемента find возвращает true, если искомый
элемент найден, и false в противном случае. Поскольку одного факта
отыскания элемента недостаточно, функция также возвращает через
список параметров два указателя: на найденный элемент p и на
предшествующий ему pp. Последний требуется при удалении элемента
из списка, поскольку при этом необходимо связывать предыдущий и
Программирование на языке высокого уровня ПаскальТ.А. Павловская
110
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
