На главную
ПРАВИЛА FAQ Помощь Участники Календарь Избранное DigiMania RSS
msm.ru
! Правила ЧаВо (FAQ) разделов Паскаля
В этом разделе разрешено создавать только темы, в которых описано РЕШЕНИЕ какой-либо общей проблемы, или описание какого-либо аспекта языка Паскаль.
Обсуждение уже созданных тем разрешено, но только конструктивное, например указание на ошибку или уточнение имеющегося текста.

Также читать Требования к оформлению статей
Модераторы: volvo877, Romtek
  
> Указатели (Pointers), Что это такое и с чем их едят
    - Указатели - Pointers -


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

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

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


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


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

      • типизированный
        ExpandedWrap disabled
          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) Задать значение указателю

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

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

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

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

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


      3) Операция разыменования
      Суть её состоит в переходе от указателя к значению, на которое он указывает. Эта операци обозначается - идентификатор указателя^ . Результатом операции является значение величины, на которую указывает указатель, т.е. осуществляется доступ к той области памяти, с которой связан указатель. Разыменованному типизированному указателю можно присваивать значения того типа, которым он описан или присваивать его значения переменным того же типа. Аналогично и для нетипизированных указателей, но только надо следить, чтобы размер памяти указанной при создании нетипизированного указателя, совпадал с размером величины, которую мы заносим в память, или в которую мы считываем значение.
      ExpandedWrap disabled
        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(Идентификатор_указателя)
      ExpandedWrap disabled
        Var
        Ptr :^integer;
        ............
        new(ptr);
        ............
        dispose(ptr);
      После выполнения этой процедуры значение типизированнго указателя неопределено и теряется значение, на которое он указывал. Дальнейшее использование этого указателя возможно, только после присваивания ему значения другого указателя или повторного применения процедуры New. Процедуру Dispose можно применять только к типизированным указателям значение, которых определено.

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

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

        с типизированными указателями
        В этом примере приводятся способы использования типизированных указателей:
        ExpandedWrap disabled
          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.


        с нетипизированными указателями
        В этом примере считывается файл в буффер с применением нетипизированного указателя и выводит количество цифр в нём.
        ExpandedWrap disabled
          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.


        Скачать исходники:
        Сообщение отредактировано: Romtek -

        Прикреплённый файлПрикреплённый файлpointer1.zip (1.22 Кбайт, скачиваний: 208)
        0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
        0 пользователей:


        Рейтинг@Mail.ru
        [ Script Execution time: 0,1085 ]   [ 18 queries used ]   [ Generated: 18.07.19, 01:13 GMT ]