Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Nagima_gostin_barlik_jauaptari 2.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
887.84 Кб
Скачать

1. Объявление указателей

Как правило, в Турбо-Паскале указатель связывается с некоторым типом данных (типизированныеуказатели).

Type    PerPnt = ^PerRec;{указатель на переменную типа PerRec}    PerRec = Record      Name: String;      Job: String;      Next: PerPnt;    End;  Var    P1: ^Integer;    P2: ^Real;

Внимание! При объявлении типа PerPnt мы сослались на PerRec, который ещё не был объявлен, это связано с тем, что существует исключение из правил для указателей, которые содержат ссылку на ещё неописанный тип данных. Это исключение сделано не случайно, так как динамическая память позволяет использовать организацию данных в виде списков (каждый элемент имеет в своём составе ссылку на соседний элемент - обратите внимание на поле Next).

В Турбо-Паскале можно объявлять указатель и не связывать его с конкретным типом данных:  Var   PP: Pointer;

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

В Турбо-Паскале можно передавать значения только между указателями, связанными с одним и тем же типом данных.  Var   P1,P2: ^Integer;   P3: ^Real;   PP:Pointer; Begin   P1:= P2; {- верно}   P1:= P3; {- неверно}

но можно сделать так:    PP:= P3;    P1:= PP;  так как ограничение не распространяются на нетипизированные указатели. Но подобные операции часто путают программиста и чреваты смысловыми ошибками.

2. Выделение и освобождение динамической памяти

Вся динамическая память – пространство ячеек, называемое кучей. Физически куча располагается в старших адресах, сразу за программой. Указатель на начало кучи храниться в предопределенной переменной HeapOrg, конец - FreePtr, текущую границу незанятой динамической памяти указывает указатель HeapPtr. Для выделения памяти под любую переменную используется процедура New. Единственным параметром является типизированный указатель:  Var   I,J: ^Integer;   R: ^Real;  Begin   New(I); {под I выделяется область памяти,}   {адрес первого байта этой области помещается в I} End.

После выполнения этого фрагмента указатель I приобретёт значение, которое перед этим имел указатель кучи HeapPtr, а HeapPtr увеличится на два (т.к. он типа Integer); New(R) - вызовет смещение указателя на 6 байт. После того как указатель приобрёл некоторое значение, то есть стал указывать на конкретный байт памяти, по этому адресу можно разместить значения соответствующего типа:

  I^:= 2;    R^:= 2*Pi;  Допустима запись: R^:= Sqr (R^) + I^ - 17;  Но недопустима запись: R:= Sqr (R^) + I^ - 17; так как указателю R нельзя присвоить значение вещественного выражения.

Возврат динамической памяти обратно в кучу осуществляется оператором Dispose:    Dispose(R);    Dispose(I); - вернут в кучу, ранее забранные 8 байт.    Dispose(Ptr) не изменяет значения указателя Ptr, а лишь возвращает в кучу память, связанную с этим указателем. Однако повторное применение процедуры к “свободному” указателю приведет к возникновению ошибки времени исполнения. Чтобы указать, что указатель свободен, нужно использовать зарезервированное слово Nil.

К указателям можно применять операции отношения, в том числе и сравнения с Nil:  Const  P:^Real = Nil; . . . . . . . . Begin  If P = Nil then    New (P);  . . . . . . . .  Dispose(P);  P:= Nil; End.

Использование указателей, которым не присвоено значение процедурой New или каким-либо другим способом, никак не контролируется системой и может привести к непредсказуемым результатам. Многократное чередование New и Dispose приводит к “ячеистой” структуре кучи. Дело в том, что все операции с кучей выполняется под управлением особой программы, которая ведёт учёт всех свободных фрагментов в куче. При выполнении оператора New( ) эта программа отыскивает минимальный свободный фрагмент, в котором может разместиться требуемая переменная. Адрес начала найденного фрагмента возвращается в указателе, а сам фрагмент или его часть нужной длины, помечаются как занятая часть кучи.

Можно освободить целый фрагмент кучи следующим образом:  1. Перед началом выделения динамической памяти значения указателя HeapPtr запоминается в переменной-указателе с помощью процедуры Mark.  2. Выполнение программы.  3. Освобождение фрагмента кучи от заполненного адреса до конца динамической памяти с использованием процедуры Release.

Var  P, P1, P2, P3, P4, P5: ^Integer; Begin  New(P1);  New(P2);  New(P3);  New(P4);  New(P5);  Mark(P); . . . . . . . .   Release (P); End.

В этом примере процедурой Mark(P) в указатель P было помещено текущее значение HeapPtr, однако память под переменную не резервировалась.

Вызов Release уничтожает список свободных фрагментов в куче, созданных Dispose, поэтому совместное применение этих процедур не рекомендуется.

Для работы с нетипизированными указателями используются также процедуры GetMem(P, Size) иFreeMem(P, Size) - резервирование и освобождение памяти.  P - нетипизированный указатель, Size - размер.

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

Использование нетипизированных указателей даёт широкие возможности неявного преобразования типов:  Var    i,j: ^Integer;    r: ^Real;  Begin    New (i); { I:= HeapOrg; HeapPtr:= HeapOrg+2 }    j:= i; { J:= HeapOrg }    j^:=2;    Dispose (i); { HeapPtr:= HeapOrg }    New (r); { R:= HeapOrg; HeapPtr:= HeapOrg+6 }    r^:= Pi;    WriteLn (j^);  End.  {Будет выведено: "8578"}  {здесь преобразование не имеет никакого смысла}

Примеры использования указателей. Динамическая память - 200..300 Кбайт. Нужно разместить массив 100 * 200 типа Extended. Требуется 100 * 200 * 10 = 200000 байт. Пробуем:  Var   i,j: Integer;   PtrArr: Array[1..100, 1..200] of ^Extended.  Begin . . . . . .   For i:= 1 to 100 do     For j:= 1 to 200 do      New (PtrArr [i,j]); . . . . . . End.

Теперь к любому элементу можно обратиться: PtrArr[i,j]^:=...; Но длина внутреннего представления указателей 4 байта, поэтому потребуется ещё 100*200*4 = 80000 байт, что превышает размер сегмента (65536 байт), доступный для статического размещения данных.

Можно было бы работать с адресной арифметикой (арифметикой над указателями), то есть не создавать массив указателей, а вычислять адрес элемента непосредственно перед обращением к нему. Однако в Турбо-Паскале над указателями не определены никакие операции, кроме присваивания и отношения. Но задачу решить можно:  Seg(x) - возвращает сегментную часть адреса.  Ofs(x) - возвращает смещение.  x - любая переменная, в том числе и та, на которую указывает указатель.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]