
- •Содержание
- •Введение
- •Структуры данных Классификация структур данных
- •Операции над данными
- •Понятие алгоритма
- •Массивы Описание массива
- •Представление массивов в памяти
- •Рис 1. Представление вектора в памяти
- •Рис 2. Представление вектора ml в памяти
- •Алгоритмы поиска
- •Алгоритмы сортировки
- •Пример сортировки простыми вставками.
- •Описание записи
- •Операции над записями
- •Записи с вариантами
- •Представление записи в памяти
- •Общие процедуры и функции для работы с файлами
- •Процедуры и функции для работы с типизированными файлами.
- •Сортировка содержимого файлов (Внешняя сортировка)
- •Пример внешней сортировки прямым слиянием
- •Пример внешней сортировки естественным слиянием
- •Динамическая память и данные с динамической структурой
- •Ссылочный тип в языке Pascal
- •Типизированные указатели
- •Нетипизированные указатели
- •Операции над переменными ссылочного типа.
- •Динамические списки
- •Реализация списков на языке Pascal.
- •Стек, очередь, дек
- •Рекурсия
- •Нелинейные структуры данных. Деревья
- •Бинарные деревья
- •Реализация бинарных деревьев
- •Способы обхода бинарных деревьев
- •Сортировка с прохождением бинарного дерева
Ссылочный тип в языке Pascal
Работа с динамическими величинами связана с использованием еще одного типа данных — ссылочного типа. Величины, имеющие ссылочный тип, называют указателями.
Указатель — это статическая переменная, которая предназначена для хранения адреса переменной, в том числе и адреса динамической переменной в Heap-области. В памяти любой указатель занимает фиксированный объём памяти — 4 байта: 2 слова: 1-ое слово — сегмент памяти, 2-ое слово — смещение.
Адрес — это номер первого байта поля памяти, в котором располагается величина.
Значение указателя — это беззнаковое целое, оно сообщает о том, где размещается переменная, и ничего не говорит о самой переменной. Значением может быть константа nil, в таком случае указатель никуда не ссылается, т.е. пуст. Это можно себе представить таким образом: в адресном пространстве оперативной памяти, выделяется один адрес, по которому заведомо не может быть размешена никакая переменная. На это место в памяти и ссылается такой пустой, или «нулевой» (0000:0000), указатель nil (зарезервированное слово). Это значение можно присваивать любому указателю.
Начало кучи хранится в стандартной переменной HeapOrg, конец - в переменной HeapEnd. На текущую границу незанятой динамической памяти ссылается указатель HeapPtr.
Замечание: ввод/вывод значения указателя не возможен!
Сами динамические величины не требуют описания в программе, поскольку во время компиляции память под них не выделяется. Во время компиляции память выделяется только под статические величины. Указатели — это статические величины, поэтому они требуют описания.
Указатели | |
Типизированные |
Нетипизированные
|
Типизированные указатели
Типизированные указатели - указатели, которые ссылаются на область данных фиксированного типа.
Для описания используется символ ^.
Схема объявления указателя:
<имя переменной-указатемя>: ^<базовый тип данных>.
Например, описание Р: ^Integer определяет указатель на динамическую целочисленную переменную.
Можно конструировать указатели на значения любого типа, последний в данном случае называется базовым типом для ссылочного типа.
Примеры объявления указателей:
Туре
Mass=array[1..100] of Integer;
p=^integer;
Var
p1: p;
i: integer;
p2: ^Integer;
p3: ^String;
pMass: ^Mass;
Память под динамическую величину, связанную с указателем, выделяется в результате выполнения процедуры New. Формат обращения к этой процедуре:
NEW(<переменная-указатемь>) .
Например, New(p1).
Для освобождения памяти от данных, потребность в которых отпала, имеется стандартная процедура Dispose. Формат обращения к этой процедуре:
DISPOSE(<указатель>) ;
Например, если динамическая переменная, связанная с указателем p1, больше не нужна, то оператор Dispose(p1) пометит область, использованную под переменную p1, как свободную. Особенно существенным становится эффект экономии памяти при удалении больших массивов.
Чередование обращений к New и Dispose обычно приводит к фрагментации — образованию ячеистой структуры памяти. При очередном обращении к New отыскивается наименьший свободный фрагмент, в котором может поместиться переменная.
С целью освобождения целого фрагмента кучи, перед началом выделения динамической памяти текущее значение указателя на границу незанятой динамической памяти запоминается в переменной указателе с помощью процедуры MARK (< переменная-указатель>). Чтобы освободить фрагмент кучи используется процедура RELEASE (<указатель>).
var
р, p1, р2: ^Integer;
Begin
New(p1);
Mark(p);
New(p2);
Release (p);
Dispose(p1);
Dispose(p2);
End.
Замечания:
!следует освобождать выделенные области сразу же после того, как необходимость в них отпадает. Иначе, происходит «засорение» памяти.
! В связи с процедурой New возможна проблема исчерпания области памяти, отведенной для динамических переменных. Если при выполнении New выяснится, что для размещения новой переменной не хватает памяти, то значение указателя, переданного в параметре не изменится. При этом выполнение программы не прекратиться и никаких сообщений выдано не будет. Поэтому работа с таким указателем может привести к непредсказуемым последствиям.
!
после выполнения Dispose
указатель
на освобождённую область памяти не
обнуляется, т.е. значение остаётся
доступным в программе
сохраняется возможность доступа к
формально уже недоступной области,
которая к моменту такого ошибочного
доступа уже может быть опять выделена
для других целей очередным обращением
к New.
! необходимо учитывать проблему, связанную с противоречием между стековым и принципом размещения статических переменных и произвольным характером создания и удаления переменных. По правилам блочности: все локальные переменные подпрограммы перестают существовать после ее завершения,
procedure someProc;
var а: integer;
р: ^string;
begin
new(p);
...
end;
Begin
writeln(memAvail);
someProc;
writeln(memAvail);
readln;
End.
Т.о. исчезает локальная переменная p, но область памяти, отведенная под нее, остается так как освободить ее можно только явно - через Dispose.