
- •Лекция 8 Введение в динамические структуры данных
- •1. Управление памятью в языках программирования
- •1.1. Статическая память
- •1.2. Автоматическая память
- •1.3 Динамическая память
- •2. Статические и динамические переменные в Паскале
- •Указатели
- •Объявление указателей
- •Выделение и освобождение динамической памяти
- •New (переменная_типа_указатель)
- •Dispose (переменная_типа_указатель)
- •Присваивание значений указателю
- •Операции с указателями
- •Присваивание значений динамическим переменным
- •Динамические структуры Линейные списки (однонаправленные цепочки).
- •Описание списка
- •Формирование списка
- •Просмотр списка
- •Удаление элемента из списка
- •Динамические объекты сложной структуры
Лекция 8 Введение в динамические структуры данных
1. Управление памятью в языках программирования
Как бы ни была велика основная память современных ЭВМ, программистам её всегда не хватает. В этой связи актуальна задача эффективного её использования. Современные языки программирования предоставляют программисту несколько механизмов управления памятью.
1.1. Статическая память
Статическая память выделяется переменным на всё время выполнения программы. Исторически это самый первый механизм "управления" памятью в ЯВУ. Метод прост и не требует никакой поддержки в ходе выполнения программы, так как адреса переменных в этом случае могут быть определены ещё на этапе компиляции программы.
В языке Turbo Pascal нет явного упоминания о статической памяти, но фактически к ней можно отнести память, в которой размещаются типизированные константы и переменные, которые описаны на уровне главной программы. Весьма поучителен следующий пример программы с использованием статической памяти.
program Static;
var i: Integer;
procedure Sub;
const Cnt: Integer = 0;
begin Inc(Cnt); WriteLn('Cnt = ',Cnt) end;
begin
for i:=1 to 3 do Sub;
end.
В этой программе типизированная константа Cnt, а фактически инициализируемая переменная, не смотря на то, что является локальной в процедуре Sub, существует всё время выполнения программы. При запуске программы её значение устанавливается равным нулю. Далее оно инкрементируется при каждом входе в процедуру Sub, и, что очень важно, не теряется после выхода из процедуры Sub вплоть до следующего входа в неё. В данном случае использование статической памяти оправдано, так как трудно придумать какой-нибудь другой способ, который бы позволил определить процедуре порядковый номер её вызова, не выходя за контекст процедуры.
1.2. Автоматическая память
Автоматическая память характерна для языков с блочной структурой. В отличие от статической она позволяет использовать одни и те же участки основной памяти для размещения разных переменных из разных программных блоков. Дело в том, что память для локальных переменных блока, выделяется только на время его выполнения. Это повышает эффективность использования памяти, но требует некоторой поддержки управления памятью в ходе выполнения программы, что несколько снижает производительность. Оказалось, что снижение производительности можно свести к минимуму, если использовать для управления памятью принцип стека, но это приводит к вложенной организации блоков программы.
Управление памятью при использовании автоматической памяти осуществляется выбором соответствующей блочной структуры программы. Для примера рассмотрим два варианта построения программы решения одной и той же задачи.
program Prgm1;
const m = 10000;
var a: array[1..m] of Integer;
b: array[1..m] of Real;
i,Sa: Integer;
Sb: Real;
begin
{ Ввод массивов a и b }
Sa:=0; Sb:=0;
for i:=1 to m do Sa:=Sa+a[i];
for i:=1 to m do Sb:=Sb+b[i];
WriteLn(Sb*Sa);
end.
program Prgm2;
const m = 10000;
var Sa: Integer;
Sb: Real;
procedure SumA;
var a: array[1..m] of Integer;
i: Integer;
begin
{ Ввод массива a }
for i:=1 to m do Sa:=Sa+a[i];
end;
procedure SumB;
var b: array[1..m] of Real;
i: Integer;
begin
{ Ввод массива b }
for i:=1 to m do Sb:=Sb+b[i];
end;
begin
Sa:=0; Sb:=0;
SumA; SumB;
WriteLn(Sb*Sa);
end.
Не смотря на то, что это абстрактные программы, они обе формально правильные и можно попытаться их выполнить. При попытке выполнить первую программу в среде Turbo Pascal мы получим сообщение об ошибке компиляции "Error 96: Too many variables". Причина ошибки заключается в том, что общий объём памяти, который выделяется переменным уровня главной программы в системе Turbo Pascal не должен превышать 64 KB. Учитывая, что переменная типа Integer занимает 2 байта, а типа Real - 6 байтов, то для нашей программы мы получим оценку 10000*(6+2) B = 80000 B = (80000 / 1024) KB = 78.2 KB, что превышает имеющиеся возможности.
Попытка выполнить вторую программу тоже приводит к сообщению об ошибке компиляции - "Error 202: Stack overflow error" (переполнение стека). Так как массивы находятся в разных процедурных блоках программы, а они вызываются последовательно, то это означает, что массивы последовательно используют одну и ту же память в стеке. Оценка необходимого объёма памяти в этом случае составляет 6*10000 B = 58.6 KB - размер большего массива. Причина ошибки состоит в том, что по умолчанию размер стека составляет всего 16 KB, но, к счастью, его можно увеличить до значения 65 520 B, что составляет почти 64 KB. Для этого в интегрированной среде надо выбрать Options | Memory sizes и установить желаемый размер стека. После этого вторая программа будет выполняться без ошибок.