Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Тема - Динамические СД.doc
Скачиваний:
2
Добавлен:
21.02.2016
Размер:
109.57 Кб
Скачать

2. Указатели

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

Указатель – это переменная, которая в качестве своего значения содержит адрес байта памяти.

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

Различают два вида указателей:

  • типизированные;

  • нетипизированные.

2.1 Типизированные указатели

Тип данных «Типизированный указатель» задает множество значений, которые соответствуют адресам переменных определенного типа данных.

Объявление переменных-указателей:

Имя указателя : ^ имя типа данных;

Например,

Type

TPoint = record

x, y, z : Real;

end;

Var

P1 : ^Integer; {указатель на целое число}

P2 : ^Real; {указатель на вещественное число}

P3 : ^Char; {указатель на символ}

P4 : ^TPoint; {указатель на точку в пространстве}

2.2 Нетипизированные указатели

Тип данных Pointer служит для объявления нетипизированных указателей, т.е. указателей, несвязанных с конкретным типом.

Var

P : Pointer; {нетипизированный указатель}

2.3 Состояния указателя:

  • неопределенное (после объявления указателя);

  • определенное (содержит адрес переменной);

  • никуда не указывает (указатель равен значению nil).

2.4 Операция присваивания указателей:

  • разрешается использовать оператор присваивания, в котором в левой части – нетипизированный указатель, а в правой части – типизированный указатель;

  • в операторе присваивания типы указателей должны совпадать.

Например.

Var

p1, p2 : ^Real;

p3 : ^Integer;

p : Pointer;

begin

p1 := p2; {real = real}

p1 := p3; {real = integer}

p := p1; {pointer = real}

p3 := p; {real = pointer}

end.

2.5 Операции разыменования и получения адреса переменной

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

Синтаксис операции разыменования:

Имя указателя ^

Для получения адреса переменной используется операция @ или addr:

@ имя переменной

addr(имя переменной)

Например.

Var

p1, p2 : ^Real;

a,b,c : Real;

begin

a := 5;

p1 := @a; {р1 указывает на а}

p2 := @b; {р2 указывает на b}

p2^ := 2; {b=2}

c := p1^ + p2^; {c = 5+2}

b := 4;

p1 := @b; {p1 указывает на b}

end.

2.6 Операции выделения и удаления динамической памяти

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

Выделение участка памяти

Для выделения участка в кучи предназначена процедура new:

New(Имя указателя);

Имя указателя = new (имя типа указателя);

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

За одно обращение к процедуре выделения памяти можно выделить не более 65 521 байт динамической памяти. Если нет свободного места в куче, то возникает ошибка выполнения программы.

Например.

Var p1, p2 : ^Integer;

P1 = new(Integer);

New (P2);

Чтобы проверить была ли выделена память, можно пойти 2 путями:

  • перед выделением памяти указателю задать значение nil;

  • перед выделением памяти проверить размер свободной части кучи с помощью функции maxavail (возвращает размер в байтах общего свободного пространства кучи).

Например: с использованием nil.

Type

TArr = array[1..100] of real;

TPArr =^TArr;

Var

p : TPArr;

begin

p := nil;

p := new (TPArr);

if (p<>nil) then

begin

{память выделена – можно выполнять обработку массива}

end

else

begin

{память не выделена – вывести соответствующее сообщение}

end;

...

end.

Например: с использованием maxavail.

Type

TArr = array[1..100] of real;

TPArr =^TArr;

Var

p : TPArr;

begin

p := nil;

if(maxavail >= sizeof(TPArr)) then

begin

p := new(TPArr);

{память выделена – можно выполнять обработку массива}

end

else

begin

{память не выделена – вывести соответствующее сообщение}

end;

...

end.

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

GetMem(нетипизированный указатель, размер выделяемого участка);

Освобождение участка памяти

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

Dispose(Имя указателя);

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

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

Также нельзя применять процедуру dispose для неопределенного указателя.

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

FreeMem(нетипизированный указатель, размер очищаемого участка);

Пример 1.

program simple;

type

TPoint3D = record

x,y,z : real;

end;

TP = ^TPoint3D;

var

a : TP;

begin

new(a);

a^.x := 1;

a^.y := 2;

a^.z := 5;

dispose(a);

end.

Пример 2.Реализация динамического массива

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

  • Sizeof(Х) – возвращает длину в байтах внутреннего представления переменной, функции или типа данных.

  • Ptr(seg,ofs) : Pointer – возвращает значение указателя по указанным сегменту и смещению;

  • Seg(Х) – возвращает сегментную часть адреса Х;

  • Ofs(Х) – возвращает смещение адреса Х;

  • Randomize – инициализирует датчик случайных чисел;

  • Random(X) – возвращает случайное значение в диапазоне от 0 до Х-1.

program dinmas;

uses crt;

var

Mas : Pointer;

n : Integer;

function CreateMas(var A : Pointer; size: integer):boolean;

begin

if (maxavail >= sizeof(Byte)*size) then

begin

GetMem(A,sizeof(Byte)*size);

CreateMas := True;

end

else CreateMas := False;

end;

procedure Vvod(A : Pointer; size : integer);

var

i : integer;

v : byte;

P : ^byte;

begin

for i:=1 to size do

begin

v := random(256);

p := ptr(seg(A), ofs(A)+sizeof(byte)*i);

p^ := v;

end;

end;

procedure Print(A : Pointer; size : integer);

var

i : integer;

v : byte;

P : ^byte;

begin

for i:=1 to size do

begin

p := ptr(seg(A), ofs(A)+sizeof(byte)*i);

v := p^;

write(v:5);

end;

end;

begin

clrscr;

randomize;

writeln('Введите количество элементов в массиве =>');

readln(n);

if (CreateMas(Mas,n)) then

begin

Vvod(Mas,n);

Print(Mas,n);

FreeMem(Mas,n);

end

else

writeln('Невозможно выделить память');

readkey;

end.

3. Классификация структур данных

Структура данных – способ представления информации.

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

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

Различаются простые структуры (типы) данных и интегрированные (структурированные, сложные).

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

Интегрированными называются такие структуры данных, составными частями которых являются другие структуры данных – простые или в свою очередь интегрированные. Интегрированные структуры данных конструируются программистом с использованием средств интеграции данных, предоставляемых языками программирования.

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

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

По признаку изменчивости различают структуры статические, полустатические и динамические.

Классификация структур данных по признаку изменчивости приведена на рис. 1.:

Рис. 1

Важный признак структуры данных – характер упорядоченности ее элементов. По этому признаку структуры можно делить на линейные и нелинейные структуры.

В зависимости от характера взаимного расположения элементов в памяти линейные структуры можно разделить на структуры с последовательным распределением элементов в памяти (векторы, строки, массивы, стеки, очереди) и структуры с произвольным связным распределением элементов в памяти (односвязные, двусвязные списки). Пример нелинейных структур – многосвязные списки, деревья, графы.

В языках программирования понятие «структуры данных» тесно связано с понятием «типы данных». Любые данные, т.е. константы, переменные, значения функций или выражения, характеризуются своими типами.

Информация по каждому типу однозначно определяет:

  1. структуру хранения данных указанного типа, т.е. выделение памяти и представление данных в ней, и интерпретирование двоичного представления;

  2. множество допустимых значений, которые может иметь тот или иной объект описываемого типа;

  3. множество допустимых операций, которые применимы к объекту описываемого типа.