Реферат Курсовая Конспект
Указатели и динамическая память - раздел Транспорт, От автора Указателями Называются Переменные И Константы, Значен...
|
Указателями называются переменные и константы, значениями которых являются адреса. Различаются два вида указателей - обобщенные указатели и типизированные указатели. Обобщенные указатели имеют тип Pointerи могут содержать адреса любых объектов. Типизированные указатели имеют тип ^базовый тип и содержат только адреса объектов базового типа. Базовый тип может быть любым, кроме файлового. Существует одна константа-указательNil, равная некоторому несуществующему адресу. Указателям можно присваивать адреса переменных, для этого служит операция “адрес”: @имя переменной. Существует и обратная операция - “значение”: указатель^, результат этой операции есть значение, записанное по адресу, который содержит указатель. Операция “значение” неприменима к обобщенным указателям. Указателям можно присваивать адреса переменных соответствующего типа и другие указатели того же типа (или обобщенные указатели). Обобщенному указателю можно присвоить любой указатель. Никакие арифметические операции к указателям не применимы, их нельзя вводить и выводить. Запишем программу, выполняющую простейшие операции с указателями:
Var
b : Byte;
w : Word;
pB : ^Byte;
pW : ^Word;
Begin
pB:=@b; {теперь в указателе pB хранится адрес переменной b, b и pB^ - одна и та же переменная}
pW:=@w; {в указателе pW хранится адрес переменной w}
Read(pB^,pW^);
WriteLn(pB^,' ',pW^,' ',b,’ ‘,w); {дважды вывели значения b и w}
End.
Как видите, конструкция указатель^ может применяться точно так же, как и имя переменной соответствующего типа. Для работы с адресами существуют четыре стандартных функции:
1.Function Addr(X): Pointer - возвращает адрес переменной X, по существу аналогична операции “адрес”.
2. Function Seg(X): Word - возвращает сегментную часть адреса переменной X.
3. Function Ofs(X): Word - возвращает смещение адреса переменной X. (Полный адрес занимает 4 байта, значение первых двух называют сегментом, а последних - смещением.)
4. Function Ptr(Seg,Ofs: Word): Pointer - возвращает адрес, состоящий из сегмента Seg и смещения Ofs. Запишем пример использования этих функций:
Const L : LongInt = 123456789;
Var p : Array [0..3] Of ^Byte;
i : Byte;
Begin For i:=0 To 3 Do p[i]:=Ptr(Seg(L),Ofs(L)+i); {теперь элементы массива p равны адресам байтов, из которых состоит константа L}
WriteLn(p[0]^:4,p[1]^:4,p[2]^:4,p[3]^:4);
End.
Программа выведет байтыL в последовательности от младшего к старшему: 21 205 91 7. Впрочем, нам известны еще два способа решения этой задачи - с помощью вариантного поля записи и с помощью описателя Absolute. Запишем соответствующие программы.
Const R : Record {вариантное поле записи}
Case Byte Of
1: (L: LongInt);
2: (b:Array[0..3] Of Byte) End
=(L:123456789);
Begin With R Do WriteLn(b[0]:4,b[1]:4,b[2]:4,b[3]:4); End.
Const L: LongInt=123456789;
Var b:Array[0..3] Of Byte Absolute L; {описатель Absolute}
Begin
WriteLn(b[0]:4,b[1]:4,b[2]:4,b[3]:4);
End.
Приведенные примеры не очень содержательны, так как указатели, главным образом, используются для работы с динамической памятью. Динамическая память (или хип) - это область памяти, которую программа может использовать для размещения динамических переменных. В отличие от обычных (или статических) переменных, память под которые отводится компилятором до начала выполнения программы и освобождается после ее завершения, динамическая память распределяется и освобождается в процессе выполнения программы. Необходимость использования динамической памяти обусловлена, в частности, ограниченностью сегмента данных и стека: все статические переменные, описанные в программе или в подпрограмме, не могут занимать более64К памяти. Динамическая память, как правило, имеет гораздо больший объем. Перечислим стандартные функции и процедуры для работы с хипом:
5. Function MemAvail : LongInt - возвращает размер свободной динамической памяти в байтах.
6. Function MaxAvail : LongInt - возвращает размер наибольшего свободного участка динамической памяти в байтах.
7. Procedure New(Var p:указатель) - отводит участок динамической памяти и присваивает указателю p адрес этого участка. Размер участка определяется базовым типом указателя.
8. Procedure Dispose(Var p:указатель) - освобождает участок динамической памяти, адрес которого хранится в указателе, после выполнения процедуры значение указателя не определено.
9. Procedure Mark(Var p:Pointer) - записывает состояние динамической памяти в указатель p.
10. Procedure Release(Var p:Pointer) - возвращает динамическую память к состоянию, записанному в указателе p.
11. Procedure GetMem(Var p:Pointer; Size: Word) - распределяет участок динамической памяти размером Size байт и записывает его адрес в указатель p.
12. Procedure FreeMem(Var p:Pointer; Size: Word) - освобождает память, распределенную процедурой GetMem.
Приведем еще две процедуры, имеющие отношение к указателям:
13. Procedure Move(Var Source,Dest; Count: Word) - копирует Count байт из переменной Source в переменную Dest, причем можно использовать и имена переменных, и указатели с операцией “значение”.
14. Procedure FillChar(Var X; Count:Word; Value) - заполняет Count байт переменной X значением Value. Value может быть либо типа Byte, либо типа Char. Приведем пример использования последних двух процедур безотносительно к динамической памяти : пусть в программе описаны массивы A длиной 100 и B длиной 1000 элементов, и требуется скопировать массивA в последние 100 элементов B, а остальные элементыB занулить:
FillChar(B,SizeOf(B),0); Move(A,B[901],SizeOf(A));
Теперь запишем программу, использующую массив, размещенный в динамической памяти:
Const Nmax=10000;
Type Massiv = Array[1..Nmax] Of Real;
Var
p : ^Massiv;
i : Word;
Begin
If MaxAvail<SizeOf(Massiv) Then Begin
WriteLn('Не хватает памяти');
Halt(0);
End;
New(p);
Randomize;
For i:=1 To Nmax Do p^[i]:=Random;
For i:=1 To Nmax Do Write(p^[i]:5);
Dispose(p);
End.
С динамическим массивом можно обращаться точно так же, как и с обычным, только вместо имени массива используется конструкция p^. Но, пользуясь подобными алгоритмами, мы все равно не можем организовать динамический массив размером более 64К байт. Пусть, например, программа должна работать с целочисленной матрицей размером 200 на 200. Оператор Type Massiv=Array[1..200,1..200] Of LongInt; является синтаксически неправильным - Паскаль запрещает описывать какие-либо объекты (и даже типы объектов), размер которых превосходит 64К байт. Применим другой способ - опишем одномерный массив из двухсот указателей, каждый из которых будет содержать адрес одной строки матрицы.
Const N=200;
Type
T_Row=Array[1..N] Of LongInt; {строка матрицы}
T_Massiv=Array[1..N] Of ^T_Row; {массив указателей на строку}
Var
p : T_Massiv;
i,j : Word;
Begin
For i:=1 To N Do GetMem(p[i],SizeOf(T_Row));{выделим память для каждой строки, и адрес этой памяти присвоим соответствующему элементу p}
Randomize;
For i:=1 To N Do
For j:=1 To N Do
p[i]^[j]:=Random(1000); {p[i] - адрес i-й строки; p[i]^ - сама i-я строка; p[i]^[j] - элемент матрицы с индексами i,j}
For i:=1 To N Do
For j:=1 To N Do Write(p[i]^[j]:4);
WriteLn;
For i:=1 To N Do FreeMem(p[i],SizeOf(T_Row));{освободим взятую нами память}
End.
Теперь, когда мы умеем пользоваться указателями, рассмотрим еще три подпрограммы модуля Graph, в свое время пропущенные нами.
Function ImageSize(x1,y1,x2,y2: Integer):Word; - возвращает размер памяти (в байтах), необходимой для хранения прямоугольного участка графического экрана, x1,y1,x2,y2 задают диагональ этого прямоугольника.
Procedure GetImage(x1,y1,x2,y2: Integer; VarBitMap); - копирует прямоугольный участок графического экрана в переменную BitMap.
Procedure PutImage(x,y: Integer; VarBitMap;WriteMode: Word); - выводит на графический экран изображение, записанное в переменной BitMap, левый верхний угол изображения совмещается с точкой x,y на экране. Параметр WriteMode имеет тот же смысл, что и в процедуре SetWriteMode. Эти средства позволяют создавать не очень сложные анимационные программы. Мы рассмотрим самый простой пример использования этих подпрограмм.
Uses Graph,Crt;
Var
Gd,Gm,i,j : Integer;
p : Pointer;
s : Word;
Const L=40;
Begin
Gd:=Detect;
InitGraph(Gd,Gm,'');
{нарисуем какую-нибудь картинку в квадрате со стороной L, например зеленый квадрат в желтой рамке с синим кружком посередине}
SetFillStyle(1,2);
Bar(0,0,L-1,L-1);
SetColor(14);
Rectangle(0,0,L-1,L-1);
SetColor(1);
SetFillStyle(1,1);
FillEllipse(L Div 2,L Div 2,L Div 4,L Div 4);
{определим размер памяти, необходимый для хранения этой картинки}
s:=ImageSize(0,0,L-1,L-1);
{выделим место в хипе}
GetMem(p,s);
{скопируем изображение в хип}
GetImage(0,0,L-1,L-1,p^);
{а теперь очень быстро заполним весь экран такими картинками}
For i:=0 To GetMaxX Div L Do
For j:=0 To GetMaxY Div L Do
PutImage(i*L,j*L,p^,CopyPut);
Repeat Until KeyPressed;
{освободим хип}
FreeMem(p,s);
CloseGraph;
End.
– Конец работы –
Эта тема принадлежит разделу:
B r... Теперь мы можем присвоить переменным их значения...
Если Вам нужно дополнительный материал на эту тему, или Вы не нашли то, что искали, рекомендуем воспользоваться поиском по нашей базе работ: Указатели и динамическая память
Если этот материал оказался полезным ля Вас, Вы можете сохранить его на свою страничку в социальных сетях:
Твитнуть |
Новости и инфо для студентов