Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > Pascal: Общие вопросы > Указатели (Pointers)


Автор: romtek 07.04.04, 15:18
- Указатели - Pointers -


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

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

Размер динамической памяти можно устанавливать из среды или из самой программы специальными директивами компилятора.
Куча первоначально всегда свободна и заполняется от нижних адресов в области кучи. За стоянием кучи можно следить при помощи специально предопределённых в языке Pascal переменных типа указатель (Pointer):
  • HeapOrg - содержит в себе адрес начала кучи, её значение не изменяется в процессе выполнения программы.
  • HeapPtr - содержит в себе начало непрерывного ещё неиспользованного участка кучи, каждый раз, когда в куче размещается новая величина значение этого указателя изменяется на размер этой переменной.
  • FreeList - указывает на конец кучи (список свободных блоков кучи).

Автор: romtek 07.04.04, 15:29
Для работы с указателями необходимо:


1. Описать указатель
2. Задать значение указателю
3. Операция разыменования
4. Освободить динамическую память


1) Описать указатель.
При этом в статической области памяти будет выделено 4 байт и связано с его идентификатором.
  • нетипизированный
    <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
      Var
      Ptr1 : Pointer;

  • типизированный
    <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
      Type
        Base = Record
          Number : Byte;
          Name : String[30];
          Phith,Bio,Math : Char;
        End;
       
        Lang = Record
          Engl,Ukr,Rus : Char;
        End;
       
        Mass = Array[1..36] Of Base;
        LstPtr = ^List;
       
        List = Record
          Number,Ifo :Integer;
        end;
        
      Var
        BegPtr,EndPtr : LstPtr;
        Ptr1 : ^Base;
        Ptr2 : ^Mass;
        Ptr3 : LstPtr;
        Ptr4 : ^String;
        Ptr5 : ^Integer;
        Ptr6 : ^Real;


2) Задать значение указателю

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Var
      K: Integer;
      P: Pointer;
    .....
    P:= Addr(K); { Присвоить адрес переменной K }
    .....

Функция Addr(X) и операция @X, возвращают значение типа Pointer, являющееся началом области памяти, в которой размещается значение переменной Х. Переменная Х - может быть любого типа.

Для выделения динамической памяти величинам при помощи типизированных указателей используется процедура New(TypePtr)
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Var
    Ptr :^integer;
    .......
    New(Ptr);
, где TypePtr - типизированный указатель. Под действием данной процедуры переменной TypePtr присваивается значение предопределённой переменной HeapPtr, в динамической памяти выделяется место соответственно типу указателя и значение переменной HeapPtr изменяется на величину этого типа. В дальнейшем при помощи этого указателя в динамической памяти можно размещать величины тип, которых соответствует типу типизированного указателя и иметь к ним доступ.

Для выделения динамической памяти величинам при помощи нетипизированных указателей используется процедура GetMem(TPtr,Size)
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Var
    Ptr:Pointer;
    ..........
    GetMem(Ptr,50);
, где TPtr - нетипизированный указатель типа Pointer, Size - величина типа Word, количество выделяемой динамической памяти. Под действием данной процедуры переменной TPtr присваивается значение предопределённой переменной HeapPtr, в динамической памяти выделяется участок размером Size байт, значение переменной HeapPtr изменяется на величину параметра Size. В дальнейшем при помощи этого указателя в выделенном учаске динамической памяти можно размещать любые величины размером Size байт и иметь к ним доступ.

Для определения количества памяти необходимой для размещения величины какого - либо типа, в языке Pascal определена функция SizeOf(X).
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Var sameVar: byte;
      K:word;
    ..........
    K=SizeOf(sameVar); {k=1}
    ..........
, где X - идентификатор переменной, функции или типа. В качестве своего результата функция возвращает величину типа Word - количество памяти в байтах необходимое для размещения X.


3) Операция разыменования
Суть её состоит в переходе от указателя к значению, на которое он указывает. Эта операци обозначается - идентификатор указателя^ . Результатом операции является значение величины, на которую указывает указатель, т.е. осуществляется доступ к той области памяти, с которой связан указатель. Разыменованному типизированному указателю можно присваивать значения того типа, которым он описан или присваивать его значения переменным того же типа. Аналогично и для нетипизированных указателей, но только надо следить, чтобы размер памяти указанной при создании нетипизированного указателя, совпадал с размером величины, которую мы заносим в память, или в которую мы считываем значение.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Var
    I: ^Integer;
    R: ^Real;
    Begin
    New( I );
    New( R );
    I^:=200;
    R^:=3.1456;
    Writeln(I^); Writeln(R^); {На экран будут выведены значения 200 и 3.1456}
    End.



4) Освободить динамическую память

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

Для типизированных указателей определена процедура Dispose(Идентификатор_указателя)
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Var
    Ptr :^integer;
    ............
    new(ptr);
    ............
    dispose(ptr);
После выполнения этой процедуры значение типизированнго указателя неопределено и теряется значение, на которое он указывал. Дальнейшее использование этого указателя возможно, только после присваивания ему значения другого указателя или повторного применения процедуры New. Процедуру Dispose можно применять только к типизированным указателям значение, которых определено.

Для нетипизированных указателей определена процедура FreeMem(P, Size)
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    Var
    ptr :Pointer;
    ............
    GetMem(ptr,67); ............
    FreeMem(ptr,67);

, где P указатель типа Pointer, Size величина типа Word (количество освождаемой динамической памяти). Под действием данной процедуры освобождается участок памяти, начиная с адреса находящегося в указателе P, размером.Size. Значение указателя становится неопределённым. Данную процедуру нельзя применять к указателям значение, которых неопределено и следует внимательно следить за размером освобождаемой памяти. Применение этих процедур не изменяет значение переменной HeapPtr ,а освобождаемая динамическая память не учитывается как неиспользованная. На неё не будут указывать указатели при последующих вызовах процедур: New или GetMem.

Автор: Romtek 14.07.04, 11:18
Вот вам и примеры ;)

с типизированными указателями
В этом примере приводятся способы использования типизированных указателей:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    type
       TInfo = record
         Size,
         Len: integer;
         ss:  string[20];
       end;
       Arr = array[1..10] of word;
     
    var
       PInteger: ^Integer; { указатель на тип Integer }
       PInfo:    ^TInfo;   { указатель на тип TInfo}
       PA:       ^Arr;     { указатель на тип Arr}
       P:        Pointer;
       Sz,i:     word;
     
    begin
     
         writeln;
         New (PInteger); { выделение памяти под указатель на тип Integer }
          PInteger^ := 123; { значению по адресу PInteger зададим значение }
          writeln ('Value = ', PInteger^);
         Dispose (PInteger); { освобождение зарезервированной памяти }
     
     
         New (PA); { выделение памяти под указатель на тип Arr }
          for i := 1 to 10 do PA^[i] := i * 2 - 1; { значениям массива по адресу PA зададим значения }
          for i := 1 to 10 do write (PA^[i] : 4); { выведем элементы массива по адресу PA }
         Dispose (PA); { освобождение зарезервированной памяти }
     
         New (PInfo); { выделение памяти под указатель на тип TInfo}
          with PInfo^ do { заносим данные в запись по адресу PInfo }
          begin
              writeln;
              { вводим значения переменных в записи }
              write ('Size: '); readln (Size);
              write ('Length: '); readln (Len);
              write ('String: '); readln (ss);
     
              writeln (Size:6, Len:6, ss:20); { выводим их значения }
          end;
         Dispose (PInfo); { освобождение зарезервированной памяти после использования записи }
     
    end.


с нетипизированными указателями
В этом примере считывается файл в буффер с применением нетипизированного указателя и выводит количество цифр в нём.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    var
         Digits: word;
     
    procedure AnalizeBlock (Buf: pointer; Sz: word);
    type
         ByteArr = Array [1..MaxInt] of char;
         PByteArr = ^ByteArr;
     
    var
         i: word;
         P: PByteArr;
     
    begin
         P := PByteArr (Buf);
         for i := 0 to Sz - 1 do
         begin
              if P^[i] in ['0'..'9'] then
                inc (Digits);
         end;
    end;
     
    var
       Buffer: Pointer;
       F:      file;
       Size,
       Count:  word;
     
    begin
         writeln;
     
         Assign (F, 'test.dat');
         {$I-}
         Reset (F, 1);
         {$I+}
         if IOresult <> 0 then
              Halt (-1);
     
         Digits := 0;
         Size   := 4096;
     
         GetMem (Buffer, Size);
         if Buffer <> Nil then
         begin
              while Not EOF (F) do
              begin
                   BlockRead (F, Buffer^, Size, Count);
                   AnalizeBlock (Buffer, Size);
              end;
              FreeMem (Buffer, Size);
         end;
     
         Close (F);
     
         writeln ('File contains ', Digits, ' digits.');
    end.


Скачать исходники:

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)