Опубликован: 03.09.2010 | Уровень: для всех | Доступ: платный
Лекция 5:

Работа с динамической памятью

< Лекция 4 || Лекция 5: 1234 || Лекция 6 >
Аннотация: Указатели: виды, описание, использование. Динамические переменные. Динамические структуры данных: стек, очередь, линейный список, бинарное дерево.

Презентацию к данной работе Вы можете скачать здесь.

В PC-совместимых компьютерах память условно разделена на сегменты. Компилятор формирует сегменты кода, данных и стека, а остальная доступная программе память называется динамической (хипом, кучей ). Ее можно использовать во время выполнения программы.

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

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

Указатели

Имя переменной служит для обращения к области памяти, которую занимает ее значение. Программист может определить собственные переменные для хранения адресов областей памяти. Такие переменные называются указателями. В указателе можно хранить адрес данных или программного кода (например, адрес точки входа в процедуру). Адрес занимает четыре байта и хранится в виде двух слов, одно из которых определяет сегмент, второе — смещение.

Указатели в Паскале можно разделить на два вида: стандартные и определяемые программистом. Величины стандартного типа pointer предназначены для хранения адресов данных произвольного типа, например:

var p : pointer;

Программист может определить указатель на данные или подпрограмму конкретного типа. Как и для других нестандартных типов, это делается в разделе type:

type pword = ^word; { читается как 'указатель на word' }
...
var pw : pword;

Здесь определяется тип pword как указатель на величины типа word. В переменной pw можно хранить только адреса величин указанного типа. Такие указатели называются типизированными. Можно описать указатель на любой тип данных, кроме файловых.

Аналогично другим типам, тип указателя на данные можно описать и непосредственно при описании переменной, например:

var pw : ^word;

Операции с указателями

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

  • Любому указателю можно присвоить стандартную константу nil, которая означает, что указатель не ссылается на какую-либо конкретную ячейку памяти.
  • Указатели стандартного типа pointer совместимы с указателями любого типа.
  • Указателю на конкретный тип данных можно присвоить только значение указателя того же или стандартного типа.

Операция @ и функция addr позволяют получить адрес переменной, например:

var x  : word;        { переменная }
    pw : ^word;       { указатель на величины типа word }
...
pw := @w;             { или pw := addr(w); }

Для обращения к значению переменной, адрес которой хранится в указателе, примеряется операция разадресации ( разыменования ), обозначаемая с помощью символа ^ справа от имени указателя, например:

pw^ := 2;   
inc(pw^);    
writeln(pw^);

В первом операторе в ячейку памяти, адрес которой хранится в переменной pw, заносится число 2. При выполнении оператора вывода на экране появится число 3.

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

ПРИМЕЧАНИЕ Указатели стандартного типа разыменовывать нельзя.

Указатели можно сравнивать на равенство и неравенство, например:

if p1 = p2 then ...
if p <> nil then ...

Динамические переменные

Динамические переменные создаются в хипе во время выполнения программы с помощью подпрограмм new или getmem. Динамические переменные не имеют собственных имен — к ним обращаются через указатели.

  • Процедура new (var p : тип_указателя) выделяет в динамической памяти участок размера, достаточного для размещения переменной того типа, на который ссылается указатель p, и заносит в него адрес начала этого участка.
  • Функция new(тип_указателя) : pointer выделяет в динамической памяти участок размера, достаточного для размещения переменной базового типа для заданного типа указателя, и возвращает адрес начала этого участка.

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

  • Процедура getmem (var p : pointer; size : word) выделяет в динамической памяти участок размером в size байт и присваивает адрес его начала указателю p. Эту процедуру можно применять и для указателей типа pointer, поскольку количество выделяемой памяти задается в явном виде.

Если выделить требуемый объем памяти не удалось, программа аварийно завершается.

Рассмотрим пример работы с динамическими переменными. Определим в разделе описания переменных главной программы три указателя p1, p2 и p3.

type rec = record
        d : word;
        s : string;
     end;
     pword = ^word;
var  p1, p2 : pword;
     p3     : ^rec;

Это — обычные статические переменные, компилятор выделяет под них в сегменте данных по четыре байта и обнуляет их ( рис. 5.1).

 Размещение указателей в памяти

Рис. 5.1. Размещение указателей в памяти

В разделе исполняемых операторов программы запишем операторы:

new(p1); p2 := new(pword); new(p3);

В результате выполнения процедуры new(p1) в хипе выделяется объем памяти, достаточный для размещения переменной типа word, и адрес начала этого участка памяти записывается в переменную p1. Второй оператор выполняет аналогичные действия, но используется функция new. При вызове процедуры new с параметром p3 в динамической памяти будет выделено количество байтов, достаточное для размещения записи типа rec.

Доступ к выделенным областям осуществляется с помощью операции разадресации:

p1^ := 2; p2^ := 4; p3^.d := p1^; p3^.s := 'Вася';

В этих операторах в выделенную память заносятся значения ( рис. 5.2).

 Выделение и заполнение динамической памяти

Рис. 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 : word) освобождает участок памяти размером size, начиная с адреса, находящегося в p. Значение указателя становится неопределенным.

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

< Лекция 4 || Лекция 5: 1234 || Лекция 6 >
София Шишова
София Шишова

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

Игорь Тарасов
Игорь Тарасов
Россия, г. Астрахань
Венера Имамова
Венера Имамова
Россия