Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Динамические структуры данных 2 страница




Const ClipOn=True; ClipOff=False;

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

Пример:

Program Ex21;

Uses Graph;

Var x,y,e: integer;

Const x11=0; y11=20; x12=120; y12=65;

x21=200; y21=y11; x22=320; y22=y12;

Begin

x:=Detect; InitGraph(x,y,''); E:=GraphResult;

If E<>GrOk then writeln(GraphErrorMsg(E)) else

begin

writeln('ClipOn: ClipOff:');

Rectangle(x11,y11,x12,y12); Rectangle(x21,y21,x22,y22);

SetViewPort(x11,y11,x12,y12,ClipOn); Circle(20,20,60);

SetViewPort(x21,y21,x22,y22,ClipOff); Circle(20,20,60);

Readln;

CloseGraph

end

end.

 

Процедура GetAspectRatio. Возвращает два числа, позволяющие оценить соотношение сторон экрана. Формат обращения:

GetAspectRatio(x,y)

Здесь x, y - переменные типа Word. Значения, возвращаемые в переменных x и y, позволяют вычислить отношение сторон графического экрана в пикселях. Этот коэффициент может использоваться при построении правильных геометрических фигур, таких как окружности, квадраты и т.п.

Процедура SetAspectRatio. Устанавливает масштабный коэффициент отношения сторон графического экрана. Формат обращения:

SetAspectRatio (x,y)

Здесь x,y - устанавливаемое соотношение сторон.

В следующем примере программа строит 10 окружностей при разных соотношениях сторон экрана.

Пример:

Program Ex22;

Uses Graph,Crt;

Const P=50; dx=700;

Var D, R, E, i: integer; Xasp, Yasp:word;

Begin

D:=Detect; InitGraph(D, R,''); E:=GraphResult;

If E<>GrOk then writeln(GraphErrorMsg(E)) else

begin

GetAspectRatio(Xasp,Yasp);

For i:=1 to 20 do

begin

SetAspectRatio(Xasp+i*dx, Yasp);

Circle(GetMaxX div 2, GetMaxY div 2, P)

end;

while not KeyPressed do;

CloseGraph

end

End.

 

Процедура PutPixel. Выводит заданным цветом пиксель по указанным координатам. Формат обращения:

PutPixel(x,y,<цвет>)

Здесь x,y - выражения типа Integer, определяющие координаты;

<цвет> - выражение типа Word, определяющее цвет.

Программа периодически выводит на экран "звездное небо" и затем гасит его.

Пример:

Program Ex23;

Uses Graph,Crt;

Type

PixelType = record x,y:integer; end;

Const n=5000; { количество звезд}

Var

D, R, E, i: integer;

a: array[1..n] of PixelType;

Begin

D:=Detect; InitGraph(D, R,''); E:=GraphResult;

If E<>GrOk then writeln(GraphErrorMsg(E)) else

begin

SetGraphMode(0); { Установить цветной pежим}

For i:=0 to N do

with a[i] do

begin

x:=Random(GetMaxX);

y:=Random(GetMaxY)

end;

Repeat

For i:=1 to N do

with a[i] do

PutPixel(x,y,succ(Random(white)));

if not KeyPressed then

For i:=1 to N do

with a[i] do PutPixel(x,y,black)

Until KeyPressed;

CloseGraph

end;

End.

 

Процедура Line. Вычерчивает линию с указанными координатами начала и конца. Формат обращения:

Line (x1,y1,x2,y2)

Здесь x1,y1 - выражения типа Integer, задающие координаты начала линии; x2,y2 - выражения типа Integer, задающие координаты конца линии.

В следующей программе экран расчерчивается случайными линиями.

Пример:

Program Ex24;

Uses Graph,Crt;

Var D, R, E: integer;

Begin

D:=Detect; InitGraph(D, R,''); E:=GraphResult;

If E<>GrOk then writeln(GraphErrorMsg(E)) else

begin

SetGraphMode(0); {Установить цветной pежим}

Repeat

SetColor(succ(Random(4)));

Line(Random(GetMaxX),Random(GetMaxY),

Random(GetMaxX),Random(GetMaxY))

Until KeyPressed;

CloseGraph

end

End.

 

Процедура SetLineStyle. Устанавливает новый стиль вычерчивания линий, Формат обращения:

SetLineStyle (<вид>,<образец>,<толщина>)

Здесь <вид> - выражение типа Word, означающее вид линии;

<образец> - выражение типа Word, указывающее образец линии;

<толщина> - выражение типа Word, задающее толщину линии.

Вид линии определяется следующими константами:

Const

Solidn = 0; {сплошная}

DottedLn = 1; {точечная}

CenterLn = 2; {штрихпунктирная}

DashedLn = 3; {пунктирная}

UserBitLn = 4; {вид линии определяется пользователем}

Параметр <образец> учитывается только для линий, вид которых определяется пользователем. Параметр <толщина> может принимать одно из двух значений:

Const

NormWidth = 1; {толщина в 1 пиксель}

ThickWidth = 3; {толщина в 3 пикселя}

Установленный процедурой стиль линий (текущий стиль) используется также при построении прямоугольников и многоугольников.

В примере демонстрируются линии всех стандартных стилей.

Пример:

Program Ex25;

Uses Graph,Crt;

Const

Style: array[0..4] of string[9] = ('SolidLn', 'DottedLn', 'CenterLn',

'DashedLn', 'UserBitLn');

Width: array[0..1] of string [11] = ('NormWidth:', 'ThickWidth:');

Var D, R, E, i,j: integer; p:word;

Begin

D:=Detect; InitGraph(D, R,''); E:=GraphResult;

If E<>GrOk then writeln(GraphErrorMsg(E)) else

begin

DirectVideo:=false;

For j:=1 to 1 do

begin

OutTextXY(0,j*40,width[j]);

For i:=0 to 3 do

begin

SetLineStyle(i,0,j*2+1);

Line(0, i*8+j*40+12, 100, i*8+j*40+12);

OutTextXY(168,j*40+12+i*8,style[i])

end

end;

repeat until keypressed;

CloseGraph

end

End.

 

Процедура SetWriteMode. Устанавливает способ взаимодействия вновь выводимых линий с уже существующим на экране изображением. Формат обращения:

SetWriteMode (<способ>)

Здесь <способ> - выражение типа Integer, задающее способ взаимо-действия вновь выводимых линий с изображением. Если параметр <способ> имеет значение 0, выводимые линии накладываются на существующее изображение обычным образом. Если значение 1, то это наложение осуществляется с применением логической операции XOR (исключительное или): в точках пересечения выводимой линии с имеющимся на экране изображением светимость пикселей инвертируется на обратную, так что два следующих друг за другом вывода одной и той же линии на экран не изменят его вид. Для задания параметра <способ> можно использовать предварительно определенные в модуле константы:

Const

CopyPut = 0; {наложение оператором MOV}

XORPut = 1; {наложение оператором XOR}

В примере имитируется вид часового циферблата с бегущей секундной стрелкой:

Пример:

Program Ex26;

Uses Graph, Crt;

Var D, R, E, i, x0,y0,x1,y1,x2,y2: integer; Xasp,Yasp:word;

Const dr=0.9;

Begin

D:=Detect; InitGraph(D, R,''); E:=GraphResult;

If E<>GrOk then writeln(GraphErrorMsg(E)) else

begin

x0:=GetMaxX div 2; y0:=GetMaxY div 2;

GetAspectRatio(Xasp,Yasp);

r:=y0; Circle(x0,y0,r);

Circle(x0,y0,Round(r*dr));

For i:=0 to 59 do

begin

x1:=x0 + Round(dr*r*sin(2*3.14*i/60));

x2:=x0 + Round(r*sin(2*3.14*i/60));

y1:=y0 - Round(dr*r*Xasp*cos(2*3.14*i/60)/Yasp);

y2:=y0 - Round(r*Xasp*cos(2*3.14*i/60)/Yasp);

Line(x1,y1,x2,y2)

end;

FloodFill(x0,y0,white);

SetWriteMode(XORPut);

repeat

For i:=0 to 59 do

if not KeyPressed then

begin

x2:=x0+Round(dr*r*sin(2*3.14*i/60));

y2:=y0-Round(dr*r*Xasp*cos(2*3.14*i/60)/Yasp);

Line(x0,y0,x2,y2);

Delay(1000);

Line(x0,y0,x2,y2)

end

Until KeyPressed

end

end.

 

8.5.3. Сохранение и выдача изображения

Процедура GetImage. Помещает в память копию прямоугольного фрагмента изображения. Формат обращения:

GetImage(x1,y1,x2,y2,<буфер>)

Здесь <буфер> - переменная или участок кучи, куда будет помещена копия видеопамяти с фрагментом изображения.

Процедура PutImage. Выводит в заданное место экрана копию фрагмента изображения, ранее помещенную в память процедурой GetImage. Формат обращения:

PutImage(x,y,<буфер>,<вид>)

Здесь x,y - выражения типа Integer, определяющие координаты левого верхнего угла того места на экране, куда будет скопирован фрагмент изображения;

<буфер> - переменная или участок кучи, откуда будет копироваться изображение;

<вид> - выражение типа Word, определяющее способ копирования.

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

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

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

Const

NormalPut = 0;{замена существующего изображения на копию}

XorPut = 1;{исключительное ИЛИ}

OrPut = 2;{объединительное ИЛИ}

AndPut = 3;{логическое И}

NotPut = 4;{инверсия изображения}

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

Программа рисует "летающую тарелку" на звездном фоне.

Пример:

Program Ex27;

Uses Graph, CRT;

Label loop;

Const

r=20; {хаpактеpный pазмеp таpелки}

pause = 10;{длительность паузы}

col = white;{цвет таpелки}

Var d,m,e:integer;

xm,ym,x,y,lx,ly,rx,ry,size,i,dx,dy,width,heigth:integer;

saucer:pointer;

Begin

d:=Detect; InitGraph(D, m,''); E:=GraphResult;

If E<>GrOk then writeln(GraphErrorMsg(E)) else

begin

SetGraphMode(0);

x:=r*5; y:=r*2;

xm:=GetMaxX; ym:=GetMaxY;

{Создать блюдце из двух эллипсов с усами антенн}

SetColor(col);

Ellipse(x,y,0,360,r,r div 3+2); Ellipse(x,y-4,190,357,r,r div 3);

Line(x+7, y-6, x+10, y-12); Line(x-7, y-6, x-10, y-12);

Circle(x+10,y-12, 2); Circle(x-10, y-12, 2);

SetFillStyle(SolidFill,col);

FloodFill(x+1, y+4, col);

{Опpеделить его габаpиты и поместить в кучу}

lx:=x-r-1; ly:=y-14; rx:=x+r+1; ry:=y+r div 3 +3;

width:=rx-lx+1; heigth:=ry-ly+1; size:=ImageSize(lx,ly,rx,ry);

GetMem(saucer,size); GetImage(lx,ly,rx,ry,saucer^);

{Стеpеть постpоенное}

PutImage(lx,ly,saucer^,XorPut);

{Создать звездное небо}

For i:=1 to 1000 do

PutPixel(Random(xm), Random(ym), Random(succ(GetMaxColor)));

x:=xm div 2; y:=ym div 2;

dx:=GetMaxX div 100 - Random(GetMaxX div 50);

dy:=GetMaxY div 40 - Random(GetMaxY div 20);

{Основной цикл: вывести-пауза-стеpеть}

Repeat

PutImage(x,y,saucer^,XorPut);

Delay(pause);

PutImage(x,y,saucer^,XorPut);

{получить новые кооpдинаты}

loop:

x:=x+dx; y:=y+dy;

if(x<0) or (x+width+1>xm) or(y<0) or (y+heigth+1>ym) then

begin

x:=x-dx;

dx:=GetMaxX div 10 - Random(GetMAxX div 5);

dy:=GetMaxY div 40 - Random(GetMaxY div 20);

Goto loop

end

Until KeyPressed;

CloseGraph

end

End.

 

Вопросы для самоконтроля:

1. Что такое модуль?

2. Структура модуля.

3. Виды стандартных модулей.

4. Назначение модуля Graph.

5. Назначение модуля Crt.

6. Назначение модуля Printer.

7. Назначение модуля DOS.

8. Назначение модуля Overlay.

9. Что такое оверлей?

10. Как расчитать размер оперативной памяти, выделяемой под оверлейную программу?

11. Как идентифицируются оверлейные модули в программе?

12. Как осуществляется инициализация оверлея?

13. Основные процедуры и функции оверлея?

14. Как происходит инициализация графического режима?

15. Процедуры и функции построения графических изображений.

16. Как происходит сохранение и выдача изображения?


ГЛАВА 9

 

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

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

Var a:array[1..300,1..300] of integer;

Один элемент матрицы - целое число занимает в памяти машины 2 байта. Общее количество элементов составляет 300*300=90 000, откуда следует оценка объема памяти, необходимой для размещения всего массива: 180 Кбайт. Нужно заметить, попутно, что в памяти также приходится размещать и программу пользователя, и компилятор, а также все переменные, используемые программой.

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

Один сегмент всегда необходим для размещения главной программы.

Если программа имеет модульную структуру, то дополнительно по одному сегменту выделяется для каждого модуля. Кроме того, память обязательно выделяется под системный модуль System. Все эти сегменты носят название сегментов кода. Еще один сегмент необходим для размещения глобальных переменных и типизированных констант. К ним относятся переменные и константы, объявленные в главной программе и в секциях связей модулей. И, наконец, последним является сегмент стека, служащий в основном для размещения локальных данных подпрограмм и внутренних данных модулей. Сегменты программы располагаются в памяти в определенном порядке:

 

Динамическая память
Оверлейный буфер
Сегмент стека
Сегмент данных
Сегмент кода модуля System
Сегмент кода 1 модуля программы
...
Сегмент кода последнего модуля
Сегмент кода главной программы

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

Вспомним программу, которая создавала базу данных по студентам класса. Там мы определяли сравнительно небольшой массив записей с данными (30 элементов). Предположим теперь, что нам надо создать базу для набора абитуриентов. Придется увеличивать размер массива. Но насколько? Допустим, сегодня будет достаточно 100 записей. А если завтра потребуется 1000, 5000 или того больше? Конечно, было бы верхом расточительности предусматривать массив из 5000 элементов, когда реально их нужно значительно меньше.

 

9.1. Динамическое распределение памяти

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

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

Оставшаяся свободной после размещения сегментов область (по схеме распределения памяти) частично или полностью может быть занята динамической памятью.

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

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

Указатели важны не сами по себе, а лишь постольку, поскольку с их помощью можно работать с динамическими переменными. Динамическая переменная явно не объявляется. Обращение к динамической переменной формируется из имени переменной-указателя, за которым следует символ ^.

Значение переменной ссылочного типа можно присвоить с помощью операции @. Операция @ может предшествовать имени переменной любого типа и используется для определения ее адреса.

Пример:

Program Ex28;

Var b:byte; p1:^byte;

Begin

b:=1;

p1:=@(b);

writeln ('Значение динамической пеpеменной p1^ pавно ',p1^);

end.

 

Предположим, что мы намереваемся работать с записями типа StudRec:

Type StudRec = record

name:string[20];

age: integer

end;

При этом указатель на записи типа StudRec нужно объявить следующим образом:

Type

.....

StudPtr = ^StudRec;

Var Stud:StudPtr; {Ptr - Pointer - указатель}

В первой строке сообщается, что любой объект типа StudPtr является указателем и что он будет указывать на запись типа StudRec. Символ ^ означает "указатель на...".

Во второй строке говорится, что Stud есть указатель типа StudPtr. "Машинная природа" указателя очень проста: содержащееся в нем значение есть не что иное, как адрес некоторой ячейки памяти. Установкой в указателе определенного (адресного) значения обеспечивается возможность последующего доступа к соответствующей области памяти. Сославшись на нее посредством такого указателя, мы можем с легкостью поместить туда какое-то новое значение, вывести содержимое области на печать и вообще делать с ней все то, что разрешено делать с любой поименованной переменной, объявленной обычным образом. Вместе с тем, мы никогда точно не знаем, где именно размещается наша динамическая переменная, поскольку машина сама заботится о нахождении ячейки памяти, адресуемой указателем.

 

9.2. Создание динамической переменной. Процедура new. Ссылки

Заметим, что никаких переменных типа StudRec (то есть запись-переменных) мы здесь не объявляем.

Всякий раз, когда нам нужна новая запись типа StudRec, мы должны вызвать стандартную процедуру New, передав ей в качестве параметра указатель Stud:

New(Stud);

Переменная Stud, указанная в обращении к new, имеет тип StudPtr. Результатом выполнения процедуры New будет создание в памяти компьютера переменной типа StudRec с одновременным назначением указателю Stud адреса этой переменной.

Конструкция Stud^ (то есть имя переменной-указателя с добавлением в конце символа ^) рассматривается как своеобразное имя этой записи. Иначе говоря, Stud^ - это ссылка на объект типа StudRec, выраженная через указатель Stud.

Процедура New всегда "знает", какого рода объект ей предлагается создать, поскольку в Паскале указатель должен указывать на переменную одного строго определенного типа (в нашем случае указатели типа StudPtr могут указывать только на переменные типа StudRec). Это очень важное свойство языка, обеспечивающее программе защиту от путаницы с адресацией памяти. Такое ограничение создает уверенность, что форматы данных в области памяти, к которой получен доступ, не противоречат тем операциям, которые вы намереваетесь осуществить над ней.

Как же нам называть ту переменную, которая была порождена процедурой new? Обычно мы даем имена переменным, объявляя их, но в данном случае этого мы не делали. После того, как New выполнит свою работу, мы можем упоминать любые поля вновь созданной записи по ссылке:

Stud^.Name:='Игорь Иванов';

Stud^.Age:=18;

Writeln(Stud^.Name);

Теперь представим, что мы регистрируем следующего абитуриента. Если мы попытаемся вызвать снова процедуру New, передав ей тот же указатель Stud, то первая запись при этом исчезнет, так как значением переменной-указателя является адрес, и, как во всякой переменной, помещение в нее нового значения автоматически стирает прежнее.

Для достижения цели можно применить массив, например из 100 указателей. Всякий раз, когда надо зарегистрировать очередного абитуриента, достаточно обратиться к New с соответствующим указателем - элементом массива:

Var Stud: array[1..100] of StudPtr;

..........

New(Stud[51]);

Мы начали с того, что из соображений экономии памяти отказались от массива записей, но теперь снова пришли к массиву – на сей раз из указателей. О каком же выигрыше может идти речь? Ответить на этот вопрос однозначно нельзя без точной оценки объема памяти, необходимой для размещения переменных того или иного типа. Массив из 100 указателей содержит ровно 100 объектов. В то время как в массиве из 100 записей их может быть в несколько раз больше (в зависимости от количества полей). Таким образом, чем "крупнее" переменная, адресуемая указателем, тем заметнее эффект экономии памяти от применения указателей.

 

9.3. Связные списки. Константа nil

Указатель можно заставить работать еще более гибким и эффективным образом. Для этого его нужно включить в состав записи, например, так:

Type StudPtr = ^StudRec;

StudRec = record

name:string[20];

age:integer;

nextStud:StudPtr

end;

Var StudList:StudPtr;

 

Первые две строки приведенного примера заслуживают особого внимания. Вызывает удивление тот факт, что в определении типа StudPtr фигурирует тип StudRec, определяемый позже. Это исключение из общего правила Паскаля, утверждающего, что нельзя использовать объект прежде, чем он будет объявлен.

Чего мы достигаем, поместив указатель внутрь записи? Теперь нет необходимости в массиве указателей.

При самом первом обращении к процедуре New мы передаем ей указатель StudList. Созданная при этом запись среди прочих полей содержит указатель NextStud, который можно затем передать процедуре New, чтобы она создала еще одну, вторую по счету запись:

New(StudList^.NextStud)

Но аналогичный указатель есть и в этой записи; его полное имя - StudList^.NextStud^.NextStud^. Будучи употреблено в качестве параметра обращения к New, оно позволяет получить следующую (третью) запись. Действуя подобным образом, мы постепенно выстраиваем некую цепочку записей, в которой каждая запись указывает на следующую за ней. Такого рода структура данных называется связным списком. При проходе по такому списку элемент, стоящий в самом конце (то есть не имеющий за собой следующего), должен содержать в своем поле NextStud некоторое специальное значение, ни на что не указывающее. В Паскале это значение изображается как стандартная символическая константа nil (ничего).

Правомерно, например, присваивание

Var aPtr: ^integer;

..............

aPtr:=nil;

Стандартная константа nil предназначена для изображения пустого значения указателя, то есть такого, при котором указатель не ссылается ни на какую область памяти.

 

В программировании очень важным является понятие списка. Список – это последовательность элементов (Е1, Е2, …, Еn), для которых задан порядок следования. Порядок следования может задаваться как явно, так и неявно.

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

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

 
 

 

 


Рис. 9.1 Типовое задание списка

 

Такие списки называют линейными или связными.

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

Все описанные выше указатели связаны с конкретными типами данных. Паскаль - строго типизированный язык; поэтому присваивать указателю значение другого указателя, относящегося к другому типу данных нельзя. Но это ограничивает возможности программиста. Обойти такое ограничение позволяет использование нетипизированных указателей. Для объявления переменной нетипизированным указателем нужно употреблять ключевое слово Pointer. Значением нетипизированного указателя может являться либо адрес, либо Nil.

Нетипизированному указателю может быть присвоено значение любого типизированного указателя, и наоборот. Рассмотрим пример.

Program Ex29;

Const p1:^byte=nil;

p2:^string=nil;

p3:pointer=nil;

Begin

New(p2);

p2^:='ab';

p3:=p2;

p1:=p3;

writeln(p1^)

End.

 

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

Память для размещения значений динамических переменных выделяется с помощью двух процедур New и GetMem. У процедуры New только один параметр - типизированный указатель. В отличие от New процедура GetMem имеет два параметра. Первым является нетипизированный указатель, вторым - размер резервируемой области памяти в байтах. За одно обращение к этой процедуре можно резервировать не более 64 Кбайт динамической памяти. Покажем на примере, как можно в Паскале реализовать массив с переменными границами.

Program Ex30;

Type

p=^ArrayReal;

ArrayReal=Array[1..1] of Real;

Var

Pn:pointer;

Pt:P;

i,j:integer;

Begin

Getmem(Pn,128);

Pt:=Pn;

For i:=0 to 25 do Pt^[i]:=0;

i:=11; Pt^[i]:=25;

For i:=0 to 4 do

Begin

For j:=1 to 5 do write(Pt^[i*5+j]:10:4);

writeln;

end;

End.

9.5. Стандартные процедуры для динамического управления памятью

Кроме уже известной вам процедуры New существует еще Dispose (убрать) - стандартная процедура, обратная по действию процедуре New. Вызов Dispose с некоторым указателем в качестве параметра освобождает память, связанную с данным указателем, и возвращает ее в системную область, называемую пулом свободной памяти. При этом сам указатель автоматически устанавливается в nil. Все, содержавшиеся в этой области памяти значения, теряются.

Процедура FreeMem.освобождает память, распределенную с помощью процедуры GetMem. Она использует два точно таких же параметра, как и процедура GetMem.

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

 

Program Ex31;

Var Pint1, Pint2:^integer; Preal:^Real;

Begin

New(Pint1); Pint1^:=1;

New(Preal); Preal^:=5.2;

New(Pint2); Pint2^:=0;

Dispose(Pint1); Dispose(Preal);

Writeln(Pint2^);

End.

 

После выполнения трех операторов New в начале динамической области памяти будет зарезервировано 10 байт (по 2 байта на Integer и 4 - на Real). Идущие затем операторы Dispose освободят первые шесть байт. Два следующих байта останутся занятыми значением переменной Pint2^. Значение этой переменной, равное 0, и будет выведено на экран дисплея.




Поделиться с друзьями:


Дата добавления: 2014-12-27; Просмотров: 375; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.008 сек.