Главная/Раздел  5

 

 

Главная

 

 Раздел 1

 

Раздел 2

 

Раздел 3

 

Раздел 4

 

Раздел 5

 

 

 

 

Ссылочный тип данных.
Динамическая память

Указатели.

Указатель в Turbo Pascal - это переменная, которая содержит адрес объекта определённого базового типа. При определении ссылочного типа используется базовый тип, перед которым ставится признак указателя, - символ '^'.

Например:

     Type
       mas = array[1..20] of integer;   {базовый тип}
        pm = ^mas;                      {тип-указатель на массив}
       pin = ^integer;                  {тип-указатель на целое число}

Базовым типом для ссылочного типа может быть любой тип, в том числе и ещё необъявленный, что является исключением для Turbo Pascal. То есть ссылочный тип может быть объявлен раньше, чем объявлен базовый тип.

Например:

     Type
       pm = ^mas;
      mas = array[1..20] of integer;

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

     Var
      p1, p2: pin;
          mm: ^mas;

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

Операция взятия указателя.

Если в программе объявлена некоторая переменная, например:

     Var
       i: integer;
то, как вам уже давно известно, при обращении к этой переменной по её имени мы получим содержимое этой переменной. Если же, при обращении к этой переменной, перед её именем (идентификатором) поставить специальный символ '@', то мы получим не значение данной переменной, а её адрес. Такую запись в Turbo Pascal называют операцией взятия указателя, и используют для определения ссылочных переменных:
     p1 := @i;  В результате образуется следующая связь:

         p1                                               i
     +-----------+                          +------------+
     ¦указатель  ¦------------------>¦  целое       ¦
     +-----------+                          +------------+

Операция взятия указателя допустима для любых переменных, в том числе для элементов массивов, полей записи и т.д. Например, если есть описание

     var
       A: array[1..10] of integer;
то обращение @A[i] имеет смысл указателя на i-ое целое в массиве A и также может учавствовать в присваивании:
     p1 := @A[i];

Указатель на указатель.

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

     var
       pp1: ^pin;

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

     pp1 := @p1; Образовавшиеся после этого связи выглядят так:

         pp1                             p1                      i
         +-----------+      +-----------+       +------------+
         ¦указатель  ¦-- ->¦указатель  ¦-- ->¦целое         ¦
         +-----------+      +-----------+       +------------+

"Нулевой" указатель.

Значением любой ссылочной переменной может быть не только адрес переменной базового типа, но и специальная константа - "нулевой" указатель. Нулевой указатель обозначается служебным словом nil и может быть присвоен любой ссылочной переменной. При выполнении операции присваивания

p1 := nil; значение p1 определено и равно nil, что значит что p1 не указывает ни на какую переменную базового типа.

Операции над указателями.

Над ссылочными переменными допустимы всего две операции: проверка на равенство и неравенство. Эти операции проверяют, ссылаются ли два указателя на одну и ту же переменную, и обозначаются как обычно '=' и '<>'. Например:

     ind := p1=p2;

     if p1 <> nil then   и т.д.

Операция разыменование.

Существует два способа доступа к переменным. Первый способ - обращение к переменной по её имени (идентификатору), называется прямым способом. Второй - обращение к переменной по её адресу, содержащемуся в указателе. Такой способ доступа называется косвенным.

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

Если к ссылочной переменной обратится по её имени, то мы получим адрес переменной базового типа. Если же после имени указателя поставить символ '^', то мы получим доступ к той переменной на которую указывает данный указатель. Поэтому операторы

     i := i + 1;        {прямой доступ к переменной i}
     p1^ := p1^ + 1;    {косвенный доступ к переменной i}
полностью аналогичны и выполняют одно и то же действие. Запись, которая позволяет получить доступ к переменной по указателю называется операцией разыменования. В случае "указателя на указатель" возможно многократное разыменование, например:
     pp1^^  {косвенное обращение к переменной i}
Разыменование ссылочной переменной, значением которой является nil является недопустимым, так как в этом случае не существует переменной, на которую ссылается указатель.

Статические и динамические переменные.

Все переменные, которые необходимо объявлять в разделеле объявления переменных и которые обозначаются идентификаторами, Н. Вирт называл СТАТИЧЕСКИМИ переменными. Эти переменные используются тогда, когда память, используемая программой, предсказуема (известна) в момент её написания. Распределение памяти для статических переменных производится полностью автоматически и подчинено стековой дисциплине. Но существует ряд задач, для которых невозможно предсказать размер памяти в момент написания программы. В этих задачах необходим другой подход: переменные нужно формировать динамически, не связывая их со структурой программы. Так как средств для явных описаний таких переменных нет, то обращаться к ним по их именам (идентификаторам) невозможно. Единственным способом обращения к таким переменным является использование ссылочных имена (указателей), создаваемых при формировании динамических переменных. Turbo Pascal даёт возможность как образовывать так и удалять такие переменные в любой момент работы программы, сообразуясь с потребностями решаемой задачи.

Переменные, созданием и уничтожением которых может явно управлять программист, называются ДИНАМИЧЕСКИМИ переменными.

Организация (распределение) памяти.

Во время выполнения программы оперативная память компьютера разделяется на три части:

  • область памяти в которой размещается код выполняемой программы;
  • область памяти выделяемая для статических переменных - "статическая память". Место для каждой статической переменной выделяется автоматически и подчинено стековой дисциплине - последовательному запол- нению выделенной области памяти. Поэтому статическую память иногда называют стековой памятью. Её размер по умолчанию 16К;
  • область памяти выделяемая для образования динамических переменных. Так как размещение переменных в этой области носит случайный характер, эту область часто называют heap (куча). По умолчанию динамическая память (куча) занимает весь доступный объём оперативной памяти компьютера.

Создание динамических переменных.

Для создания динамических переменных используется процедура New. Параметром данной процедуры является переменная ссылочного типа, например:

     New(p1);

При выполнении этого оператора:
1) в куче выделяется место для динамической переменной целого типа;
2) ссылочной переменной p1 присваивается указатель на только что организованную динамическую переменную.

В результате мы получаем динамическую переменную. Как вы уже поняли, имени (идентификатора) у неё нет, поэтому прямое обращение к такой переменной просто невозможно. Единственный способ, это косвенное обращение путём разыменования ссылочной переменной p1, содержащей указатель на данную динамическую переменную:

     p1^ := 28;

Процедура New может быть применена также в функциональной форме:

     p1 := New(pin);

В этом случае параметром New должен быть ссылочный тип.

Уничтожение динамических переменных.

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

     Dispose(p1);

После выполнения данной процедуры значение ссылочной переменной становится неопределённым.

Управление динамической памятью.

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

MaxAvail: longint;

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

MemAvail: longint;

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

begin
  Writeln(MemAvail, ' байт свободно');
  Writeln('Наибольший свободный блок содержит ', MaxAvail, ' байт');
end.

SizeOf

Этой функции в качестве параметра передаётся переменная или её тип; результатом является число байтов, необходимых для хранения данной переменной или переменных данного типа.

     if MaxAvail >=SizeOf(p1) then p1 := new(pin);

     Примеры программ
     ----------------

     Program ex35_1;
     Uses Crt;
     Var
       px: ^char;
        i: byte;
     Begin
       clrscr;
       new(px);
       for i:=0 to 255 do begin
        px^:=chr(i);
        write(i:3,'=',px^);
                           end;
       dispose(px);
       repeat until keypressed;
     End.

     Program ex35_2;
     Uses Crt;
     Type
       vect=array[1..10] of integer;
     Var
       px: ^vect;
        i: byte;
     Begin
      clrscr;
      new(px);
      for i:=1 to 10 do begin
        write('Введите ел-т массива N ',i,' ');
        Readln(px^[i]);
                        end;
      Writeln('Содержимое массива:');
      for i:=1 to 10 do write(px^[i]:3);
      dispose(px);
      repeat until keypressed;
     End.

Контрольные вопросы.

  1. Как объявляются ссылочные типы и ссылочные переменные?
  2. Что выполняет операция разименования?
  3. С помощью каких процедур происходит распределение памяти под динамические переменные?
  4. Какие значения может принимать ссылочная переменная?
  5. В чём различия между значением ссылочной переменной nil и неопределённым значением?
  6. Какие действия выполняют процедуры new и dispose?
  7. Как выполняется операция взятия указателя?
  8. Какие операции допустимы над указателями?
  9. Чем отличаются статический и динамические переменные?

Задания для самостоятельного выполнения.

  1. Напишите программу выбора наибольшего из четырёх введённых с клавиатуры чисел.
  2. Напишите программу изображения графика по данным, хранящимся в массиве из 15 целых чисел введённых с клавиатуры.
  3. Создайте файл состоящий из записей содержащих поля: 1) номер 2) фамилия 3) телефон. Организуйте вывод на экран монитора номера телефона по введённой с клавиатуры фамилии.
  4. Напишите программу заполнения и сортировки по столбцам массива 4x5. Вывести содержимое массива до и после сортировки.
  5. Напишите программу замены всех отрицательных элементов в массиве 4x5 заполненном с клавиатуры на их абсолютные значения. Вывести содержимое массива до и после обработки.
  6. Создайте файл состоящий из N целых чисел. Определите и выведите на экран максимальную и минимальную компоненту файла и номера их позиций в файле.
  7. Определите и выведите на экран множество символов входящих одновременно в произвольные имя, фамилию и отчество, ведённые с клавиатуры.
  8. Определите и выведите на экран множество целых чисел находящихся одновременно в строках массива 2x5 заполненного с клавиатуры.

НАЗАД                    ДАЛЕЕ

 

 

 

 

 

 

 

 

 

 :::

 

 :::

 

 

 

 

 

 

 

Сайт создан в системе uCoz