16836

Распределение оперативной памяти при выполнении программ

Лекция

Информатика, кибернетика и программирование

Лекция 9 1. Динамические переменные. Основные понятия Распределение оперативной памяти при выполнении программ Адресуемое пространство ОП в ОС MSDOS организовано сегментами: перенумерованными блоками памяти по 64 Кбайта. Причем сегмент может начинаться с любого фи

Русский

2013-06-26

756 KB

4 чел.

Лекция 9

1. Динамические переменные. Основные понятия

Распределение оперативной памяти при выполнении программ

Адресуемое пространство ОП в ОС MS-DOS организовано сегментами: перенумерованными блоками памяти по 64 Кбайта. Причем сегмент может начинаться с любого физического адреса ОП. Положение места объекта в памяти определяется с помощью номера сегмента и смещением его в сегменте, т. е. номером байта от начала сегмента. Таким образом, любая ячейка адресуемого пространства MS-DOS определяется парой чисел: СЕГМЕНТ:СМЕЩЕНИЕ. Например:

pi:   Ptr(DSEG,$58)

pi,p:   5E87:0058

Схема распределения ОП для выполнения программы на Паскале представлена на рис.1. Адреса ОП увеличиваются снизу вверх.

Рис. 1

Схема распределения ОП для программ на Паскале

При запуске программы (ЕХЕ-файла) MS-DOS организует в памяти блок длиной 256 байт с информацией о программе, называемый PSP (Program Segment Prefix). Адрес PSP, и следовательно адрес начала программы, содержится в системной переменной Pref ixSeg модуля System.

После PSP начинается ЕХЕ-файл программы. Он может занимать более одного сегмента.

Статические данные программы включают глобальные переменные, типизированные и локальные константы. Общий объем статических данных программы не может превышать 64 Кбайт.

В стеке располагаются локальные переменные и параметры-значения процедур и функций во время их работы по вызову. Паскаль отводит для стека один сегмент, поэтому стек не может превышать 64 Кбайт. Минимальный размер стека равен 1024 байтам, максимальный составляет 64 Кбайт - 8 байт = 65520 байт; по умолчанию размер стека равен 16 Кбайт = 16384 байтам. Стек заполняется от верхней границы к нижней. Для установки контроля за переполнением стека используется директива {$S+}. Если при этом в процессе выполнения программы стек будет полон, попытка разместить в нем значения при вызове очередной подпрограммы вызовет ошибку этапа выполнения программы:

202   Stack  overflow   (переполнение стека) .

Область динамических переменных называют кучей. Куча занимает всю или часть свободной ОП, оставшейся после загрузки в ОП программы. Требуемый размер стека и кучи можно установить с помощью директивы $М компилятора. Формат директивы:

{$Мстек,минимум кучи,максимум кучи}

где     стек - размер стека в байтах;

минимум кучи - по умолчанию равен 0 байт;

максимум кучи - по умолчанию равен 655360 байт; он должен быть не

менее минимума кучи.

Минимум - это размер кучи, без которого выполнение программы невозможно. Если заданный в директиве объем ОП недоступен, программа выполняться не будет. Если этот параметр равен нулю, программа будет запущена в любом случае. Максимум - это вся свободная ОП, доступная ОС. Максимальный размер кучи носит рекомендательный характер.

По умолчанию предполагается директива {$М16384 , 0 , 655360}.

Все значения задаются в десятичной или шестнадцатеричной системе счисления. Например, следующие 2 директивы идентичны:

{$М16384,0,655360} - в десятичной системе счисления; {$М$4000,$0,$А0000} - в шестнадцатеричной системе счисления.

Управление распределением (выделением и освобождением) ОП кучи при размещении динамических переменных в процессе выполнения программы осуществляет монитор кучи - одна из управляющих программ модуля System. В этом модуле объявлены переменные, используемые монитором кучи для динамического распределения ОП:

HeapOrg - указатель (адрес) начала кучи;

HeapPtr - текущий указатель кучи (адрес первого свободного байта кучи);

FreePtr - указатель списка свободных блоков (участков) ОП;

HeapError - указатель установки  адреса  процедуры  обработки ошибок при работе с кучей;

FreeMin - минимальный размер списка свободных блоков кучи;

OvrHeapEnd - указатель конца оверлейного буфера.

На рис. 2 приведена упрощенная схема организации памяти кучи.

Рис. 2

Упрошенная схема организации памяти кучи

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

Память для переменных может быть выделена статически, т. е. на этапе компиляции, или динамически, в процессе выполнения программы. В зависимости от этого и переменные называют статическими и динамическими. Память, выделенная статически, остается за переменными и константами до конца выполнения программы, даже если эти переменные и константы больше не используются в программе. Если объявлено несколько больших массивов, памяти может не хватить. Выход из положения - использование динамической памяти.

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

Динамическая память (куча) необходима в следующих случаях:

  1.  для больших массивов, которые не используются одновременно;
  2.  для массива или записи, размер которых превышает 64 Кбайт;
  3.  для массивов, требуемый объем ОП для которых определяется в процессе
    выполнения программы;
  4.  для временного запоминания данных при работе с графическими и звуковыми средствами ПК.

Достоинства динамической памяти:

  1.  экономичность и эффективность использования ОП;

возможность динамического изменения (в процессе выполнения программы) количества элементов массивов и связанных структур;

3) динамические переменные существуют в случае необходимости до окончания работы программы.

Переменная, размещаемая динамически, объявляется в разделе var как указатель на данные определенного типа.

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

В процессе компиляции исходной программы все имена переменных преобразуются в адреса ячеек памяти, в которых размещаются соответствующие значения данных. В командах машинной программы при этом стоят машинные адреса размещения значений переменных. Это прямая адресация: вызов значения по адресу в команде. Например, по оператору присваивания k:= j; на машинном уровне происходит копирование значения из области ОП, отведенной переменной j, в область ОП, отведенную переменной k. Таким образом, при выполнении машинной программы реализуются операции над операндами - значениями переменных, расположенными по определенным адресам ОП. Имена переменных в командах на машинном уровне не применяются, а только адреса, сформированные транслятором с использованием имен переменных. Но программист не имеет доступа к этим адресам, не используя указатели.

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

Переменная типа указатель занимает в ОП двойное слово (4 байта). Старшее слово содержит адрес размещения сегмента, а младшее - адрес смещения в сегменте. Указатель может быть типовым или бестиповым. Если указатель адресует определенный тип данных, он называется типовым. Бестиповый указатель может адресовать различные типы данных.

Формат объявления типа указатель:

имя-типа-указатель = ^имя-типа;

где     имя-типа-указатель - идентификатор;

имя-типа - идентификатор любого типа данных.

Ключевое слово Pointer является именем типа бестипового указателя. Примеры объявления типа указатель:

type

pi = ^integer; { - указатель на тип integer }

р2 = ^real; { -    "    -".." real }

рЗ = ^char; { -    "       "  " char }

р4=array[1..10]of ^integer;{- массив указателей на integer}

р5 = array[1..20] of ^геа1; { -    "   -   "      " real }

р6 = array[1..30] of ^сhаr;    { -    "   -   "   " char }

pp1 = ^p1; { - указатель на указатель на тип integer }

рр2 = ^р2; { -    "      "      "     "   " real }

рр3 = ^р3; { -    "      "      "     "   " char }

рр4 = ^р4; {-указатель на массив указателей на тип integer}

рр5 = ^р5; { -   "      "    "       "      "   " real}

рр6 = ^р6; { -   "      "    "       "      "   " char}

a1 = array[1..5] of integer; { - массив типа integer }

a2 = array[1..5] of real;{ -   "; "-,"   real }

a3 = array[1..6,1..50] of real;         { - }

a4 = array[1..5] of char; { -   "     "   char }

strc = record  x, у : real end; { - запись }

pa1 = а1; { - указатель на массив типа integer }

ра2 = ^а2; { -    "       "   "     "   real }

ра3 = ^а3; { - "   "     "    " }

ра4 = ^а4; { -   "      "   "    "   char }

pstr = ^strc; { - указатель на запись типа strc }

ptf = ^text; { - указатель на текстовый файл }

fn = Function ( X, Y : integer ) : real; { - имя функции}

pfn = ^fn; { - указатель на функцию типа fn }

Переменные типа указатель можно объявить одним из двух способов:

  1.  в виде указателя на стандартный или ранее определенный тип данных;
  2.  с использованием ранее определенного типа указатель.

Примеры объявления переменных типа указатель:

var  { Объявления с использованием стандартных типов: }

vpi : ^integer; { - указатель на данные типа integer }

vpr : ^real; { -     "      "   "     "   real    }

vpc : ^char; { -     "      "   "     "   char    }

{ Объявления с использованием ранее определенных типов:}

vpa1 : ^a1; { - указатель на массив типа a1 }

vpa2 : ^а2; { -          "    "     "   а2 }

vpa3 : ^а3; { -                    "   а3 }

vpa4  : ^а4; { -    "      "    "     "   а4 }

vpstr : ^strc; { - указатель на запись типа strc }

{ Объявления с использованием ранее определенных типов указателей:}

vp1 : p1; { - переменная - указатель на тип integer }

vp2 : p2; { -      "         "      "   "    real }

vp3 : рЗ; { -      "         "      "   "    char }

vp4 : p4; { - переменная - массив указателей на тип integer }

vp5 : p5; { - real }

vp6 : р6; { - "          "       "     "   "    char }

vpp4 : ^р4; { - указатель на массив указателей типа integer }

vpp5 : ^р5; { -    "      "    "                     real }

vpp6  : ^р6;  { -   "          "                    char }

vpa1 : pa1; { - указатель на массив типа integer }

vpa2 : pa2; { -     "     "    "     "     real }

vpa3 : раЗ; { -    "    "   "    "      "   }

vpa4  : pa4; { -     "     "                char }

vpstr : pstr; { - указатель на запись типа str }

pf : ptf; { - указатель на текстовый файл  }.

Ntp : pointer; { - бестиповый указатель   }

Vf : fn; { - имя функции типа fn }

Pf : pfn; { - указатель на функцию типа fn }

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

Недопустимо при объявлении типа или переменной типа указатель справа от символа ^ использовать что-либо, кроме имени типа.

Примеры недопустимых объявлений типов и переменных:

type p7 = ^string[5];

  p8 = ^array[1..10] of real;

  p9 = ^record x, у : real; end; var

vp1O : ^string [5] ;

vp11 : ^array[1..10] of real;

Правила объявления в Паскале требуют, чтобы все используемые в объявлениях константы и идентификаторы были объявлены до их использования. Но при описании указателей допускается ссылка на имя типа записи, определенной после ее использования для определения указателя. Пример такого объявления:

type  prec = ^recst; { - объявление указателя на запись }

recst = record { - объявление записи }

х, у : real;

ptr : prec;
end;

const { Инициализация: }

st: recst = (x:0;у:0;ptr:nil );     { - записи }

p1 : pointer = nil; { - бестипового указателя }

p2 : prec = nil; { - указателя на запись }

где     nil - нулевое значение указателя.

Переменной типа указатель можно присвоить значение:

  1.  адреса переменной;  с  помощью  операции РА := @А;   или  с  помощью
    функции РА  : =
    Addr  (А ) ;  ;

другого указателя; например: РА  := РВ;  ;

абсолютного адреса или константы; например: РА := NIL;  ;

с помощью процедур выделения ОП:

а) New(Var P :Pointer); - заголовок процедуры, например:
New  (РА); , где РА - имя указателя;

б) GetMem(Var P : Pointer, Size: Word); - заголовок, например:
GetMem   (   P,   m  *   sizeof (real)   ) ;, где P - имя указателя.

Обращение к переменной с помощью указателя

Указатель - это переменная или константа, которые хранят значение адреса ОП. Паскаль дает возможность использования адресов с помощью операций @ и ^.

Для объявления указателя и для извлечения значения переменной, которую адресует указатель, используется символ ^.

Для получения адреса переменной можно использовать операцию @ или функцию Addr. Операция @ - определения (получения) адреса переменной -возвращает адрес ОП своего операнда. Операндом операции @ должно быть имя переменной того же типа, для которого определен и указатель левой части оператора присваивания, получающий значение этого адреса. Например, переменная-указатель pi получит значение адреса переменной i в результате выполнения оператора: pi   :=  @i;.

Выполнение этого оператора эквивалентно pi   := Addr   (   i   );.

Рассмотрим обращение к значению динамической переменной с помощью указателя. При этом используется косвенная адресация.

Косвенная адресация значения с помощью операции ^ осуществляет доступ к значению по указателю, т. е. извлечение значения, расположенного по адресу - содержимому указателя. Операнд операции ^ должен быть типа указатель. Результат операции ^ - это значение, на которое указывает (адресует, ссылается) операнд. Тип результата - это тип, определенный при объявлении указателя.

В общем виде оператор присваивания, использующий имя указателя и ^ -операцию косвенной адресации, можно представить в виде:

имя-переменной   := имя-указателя  ^   ;

где имя-указателя - это переменная или константа, которые содержат адрес размещения значения, требуемого для переменной левой части оператора присваивания.

Например,   j  получает значение,  расположенное по адресу,  который содержится в указателе pi с помощью оператора

j   := pi ^;

Последовательность операторов:

pi:=@i; j :=pi^;

выполняет то же самое, что и оператор j := i;.

Схематично взаимосвязь между указателем и адресуемым значением представлена на рис. 3.

Рис.3

Схема взаимосвязи указателя, адреса и значения переменной

Взаимосвязь указателя и адресуемой переменной рассмотрим на примере программы, представленной в листинге 1.

Листинг 1. Формирование и использование указателей.

var 

   pi:^ integer;   { - указатель напеременную целого типа}

   j, i:integer;  { - переменные целого типа }

   рс : ^char; { - указатель на переменную типа char }

а, с :   char; { - переменные типа char }

р :   pointer; { - бестиповый указатель  }

f :   text; { - текстовый файл }

pf : ^ text; { - указатель на текстовый файл }

Begin  pf := @f; { - pf получает значение адреса f }

Assign(pf^, ''); Rewrite(pf^); { - использование pf }

i := 123;  pi := @i; { - pi получает значение адреса i }

с := 'A';  pc := @c; { - pc получает значение адреса с }

j:=pi^; {- j получает значение, расположенное по адресу pi}

а:=рс^; {- а получает значение, расположенное по адресу рс}

pi := @j; рс := @а; { - получение значений адресов j и а}

pi ^ := 33; { - переменная j получает значение 33 }

р := @ pi; { - р получает значение адреса pi }

р := @ рс; {    "  -  " рс }

р := @ р; {.._.. р }

Writeln(pf^,'i =', i,'j = ', j, '  с = ', с, '  a = ', a,

'  pi ^ = ', pi^, '  pc ^= ', pc ^); END.

Результаты выполнения программы:

i = 123  j = 123  с = A a=A pi ^ = 123  pc ^ = A

Схематично взаимосвязь указателей pi и рс, адресов и значений переменных программы листинга 1 представлена на рис. 4.

Рис. 4

Схема взаимосвязи указателей, адресов и значений переменных

В процессе выполнения программы можно остановиться на определенной строке программы и просмотреть текущие значения переменных и указателей в окне просмотра в виде номера или имени сегмента и смещения. Например:

р: PTR(DSEG,$3E)

где     DSEG - имя сегмента;

3Е - значение смещения в шестнадцатеричной системе счисления.

По результатам выполнения программы листинга 1 получены значения адресов размещения переменных программы, приведенные в табл. 1.

Таблица 1.  Значения адресов переменных программы листинга 1

Имя переменной 

Рi

j

i 

рс 

а 

с 

p 

Ее смещение в 16сс 

 

42 

44 

46 

 

 

 

Ее смещение в 10сс 

62 

66 

68 

70 

74 

75 

76 

Из значений табл. 1 видно, что значения переменных располагаются в ОП в последовательности их объявления. Переменные типа integer занимают по 2 байта, типа char - по одному байту, и указатели - по 4 байта (2 слова).

Имена типов указателей можно использовать в списках формальных параметров. Имена указателей и элементов массивов указателей с операцией ^ справа от имени переменной-указателя можно использовать:

  1.  в выражениях везде, где допускается использование значения, которое из
    влекается с помощью этой операции; например:
    j   := pi  ^; ;
  2.  в левой части операторов присваивания; например: pi^ := 33; - переменная, расположенная по адресу pi, получит значение 33.

Над указателями допускаются операции сравнения = и <>. Ввод и вывод значений указателей в Паскале недопустимы.

Многоуровневая косвенная адресация

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

Многоуровневая косвенная адресация используется при работе с указателями на указатели. При обращении к значению переменной с помощью указателя надо использовать количество символов ^, соответствующее требуемому уровню косвенной адресации.

Пример объявления указателей для различных уровней косвенной адресации:

type                                    {   Типы: }

 tpi =^integer; {- указатель на данные типа integer }

tppi=^tpi; {-указатель на указатель на данные типа integer}
tpppi = ^tppi;{-  указатель  на  указатель   на указатель

                                  на данные типа  integer}

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

  1.  полное количество символов ^, равное количеству слов указатель при объявлении, должно быть для обращения к значению адресуемой переменной;
  2.  уменьшение количества символов ^ добавляет к имени переменной слово
    указатель, причем этих слов может быть столько, сколько может быть
    уровней косвенной адресации для этих имен указателей.

Например, после объявления переменных и присваивания им значений:

Var  i:integer;

Pi  : ^integer;

Begin i:= 12; pi:= @i; ...  end.

Обращение к переменной pi в виде:

pi - определяет значение указателя на переменную i, прямая адресация, нулевой уровень косвенной адресации;

pi  ^ - определяет значение переменной i, 1-й уровень косвенной адресации.

При обращении к переменным можно использовать разное количество символов ^ для разных уровней адресации, пример:

pi, Ppi, Pppi – 0-й уровень адресации, прямая адресация;

pi^,  ppi^,  pppi^   - 1-й уровень косвенной адресации;

ppi^^, pppi^^  - 2-й уровень косвенной адресации;

pppi^^^ - 3-й уровень косвенной адресации;

Количество символов ^ определяет уровень косвенной адресации. Таким образом, к указателям 1-го и выше уровней косвенной адресации возможно обращение и с меньшим количеством символов ^ косвенной адресации, чем то, которое необходимо для получения значения конечной адресуемой переменной. Эти обращения определяют адреса, т. е. значения указателей определенного уровня адресации. Соответствие между количеством ^ при обращении с помощью указателя и назначением обращения по указателю для приведенного примера дано в табл. 2.

Таблица 2.  Соответствие   между   количеством   символов   ^    и    результатом    обрашения к значению с помошью указателя для приведенного примера

Обращение 

Результат обращения 

Уровень косвенной адресации 

i 

Значение переменной i 

0 

Pi^

Значение переменной, на которую указывает pi 

1 

Pi 

Указатель на переменную типа integer, значение pi 

0 

Ppi^^

Значение переменной типа integer 

2 

Ppi^

Указатель на переменную типа integer 

1 

Ppi 

Указатель на 'указатель на переменную типа integer', значение указателя ppi 

0 

Pppi^^^

Значение переменной типа integer 

3 

Pppi^^ 

Указатель на переменную типа integer 

2 

Pppi^

Указатель на 'указатель на переменную типа integer' 

1 

Pppi 

Указатель на 'указатель на 'указатель на переменную типа integer', значение указателя pppi 

0 

Схематично взаимосвязь между указателями и адресуемыми значениями примера представлена на рис. 5. В листинге 2 дана программа вывода значений, адресуемых указателями с различными уровнями косвенной адресации.

Листинг 2. Вывод значений, адресуемых указателями.

type {   Имя типа:    }

  tpi = ^integer;{ - указатель на тип integer }

 tppi =^tpi; { - указатель на указатель на тип integer }

tpppi = ^tppi; { - указатель на указатель на указатель

на тип integer}

var { Имя переменной типа: }

   i: integer;{ -integer}

 pi:tpi; { - указатель на тип integer }

ppi :tppi; { - указатель на указатель на тип integer }

pppi : tpppi; {- ук. на ук. на указатель на тип integer }
begin

 i:= 123; { - i получает значение 123}

 pi:= @i;{ - pi адреса  i}

 ppi:= @ pi;{ -  ppi  pi}

PPPi := @ PPi; - PPpi "   PPi )

writeln ( #10#13,

'Значение i: i = ', i:3, '   pi^ = ', рi^, '   ррi^^ = ', ppi^^, pppi^^^ = ', рррi^^^);

END.

Результаты выполнения программы:

Значение   i:   i  =   123  pi^  =   123  ppi^^   =   123  рррi^^^  =   123

Рис. 5

Схема взаимосвязи указателей и адресуемых ими значений для многоуровневой косвенной адресации скалярных значений

Из листинга 2 очевидно, что обращение к значениям i, pi, ppi возможно с использованием только указателя pppi. В листинге 3 приведен пример аналогичной программы, в которой используются только указатели.

Листинг 3. Использование только указателей для вывода значений.

type {   Имя типа  -  указатель  на:   }

     tpi  =  ^integer; {  -  тип  integer, }

   tppi  =   ^tpi; {-  указатель   на  тип  integer; }

  tpppi  =  ^tppi; {-указатель на указатель на тип  integer}

var {   Имя переменной  типа:   }

    pi : tpi; {-  указатель   на   тип  integer }

  ppi : tppi; { - указатель на  указатель на  тип  integer}

 pppi  :tpppi; { -  указ,   на указ.   на указатель  на тип  integer.}
begin

 New (pi);{ -  запрос  ОП для  значения рi^  типа  integer;

           pi  получила  значение  адреса  выделенной  ОП;

                       рi^ - динамическая переменная; }

pi ^ := 123; { - рi^  получает значение 123}

ppi := @pi; { - ppi   ''      адреса pi  }

pppi := @ ppi; { - pppi "   ppi }

writeln ( #10#13, 'Значение pi^ = ', рi^, '   ррi^^ = ', ррi^^, рррi^^^ = ', рррi^^^);

Dispose   ( pi ); { -   освобождение  ОП  pi^}

END.

Результаты выполнения программы:

Значение  pi^   =   123  ррi^ ^  =   123  рррi^^^   =   123

Отображение динамических переменных в окне просмотра

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

PTR(сегмент,смещение)

где     сегмент и смещение отображаются в шестнадцатеричной системе счисления с предшествующим символов $. Например:

uk:PTR($2E90,$10).

Если в окно просмотра к имени указателя добавить спецификатор Р, то значение указателя в окне просмотра будет представлено в виде:

uk,p:2E90:0010

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

Листинг 4. Использование динамической записи.

type

ptr_rec = ^ геc; { - имя типа - указатель на запись }

rec = record { - имя типа - записи }

nz : integer; fio : string[20]; end;

var p, t  ptr_rec; { - указатели на запись }

begin

New (p) { - выделение ОП для р^ - записи }

P^.nz =123;{- присваивание значения: - элементу nz,  }

P^.fio  = 'Иванов'; { - элементу fio, }

t = р; { -  указателю t. }

t^.nz  = 456; { - изменение значений: - nz, }

t^.fio  = 'Петров'; { - fio.}

Dispose (p);      { - освобождение ОП, выделенной переменной р^ }

р := nil; t := nil; { - обнуление значений указателей }

END.

До выделения ОП динамической переменной указатель р может содержать "мусор", случайное значение. Работа с таким значением указателя недопустима, так как запись значений по этому адресу может привести к искажению данных программы или самой ОС. Для вывода в окно просмотра текущего значения динамической переменной ее имя можно вывести в окно с уточнением (для имени массива, записи или массива записей) или без уточнения.

Если в окно просмотра вывести имена указателей и связанных с ними динамических переменных листинга 4, то после выделения ОП записи и присваивания ее элементам значений в окне Watch будут выведены:

р: PTR($62DF,$0) - значение указателя;

р, р':  62DF,0000 - значение указателя в другой форме;

р^:   (123, 'Иванов') - содержимое, расположенное по указателю;

р^,r:   (nz: 123, fio: 'Иванов') - содержимое записи с именами элементов.

После присваивания переменной t значения указателя р идентичные значения можно вывести и для переменной t. При этом 2 указателя имеют одинаковые значения и адресуют одну и ту же ОП. Изменение значений элементов записи с помощью динамической переменной t^ означает также и изменение значений динамической переменной p^, так как это фактически одна и та же переменная, как бы имеющая 2 разных имени. После освобождения ОП с помощью процедуры Dispose значения указателей не изменяются. Только после присваивания им значения nil в окно просмотра выводятся их значения, равные nil.

Для большей надежности программы целесообразно освобождать ОП, как только динамическая переменная становится ненужной, и после освобождения ОП обнулять значения указателей с помощью константы nil.

Для просмотра данных типа real динамической переменной - одномерного массива В в окно просмотра можно вывести значение указателя В или значения, например, 10 элементов динамического массива:

B:  PTR($63FE,$0)     - адрес начала размещения массива;

B^:   (1.4,23.9,123.0,... )  - значения всех объявленных элементов массива;

B^[1],2:  1.4,23.9               - значения двух элементов, начиная с первого;

B^[4],2:  47.4,563.2  - значения двух элементов, начиная с четвертого;

B^[3] :  123.0 - значение третьего элемента массива.

Проблемы, связанные с указателями

Проблемы, связанные с указателями, вызываются некорректным использованием указателей. Это может быть:

  1.  попытка работать с неинициализированным указателем, т. е. с указателем,
    не содержащим адреса ОП, выделенной переменной;
  2.  потеря указателей: переменная типа указатель получает ОП, затем она
    получает новое значение адреса без предварительного освобождения ОП,
    на которую указывает указатель; теряется значение указателя, и адресовавшаяся им ОП не может быть освобождена;
  3.  неосвобождение ОП, выделенной переменной, после того, как она не нужна; неосвобожденная ОП считается занятой, что может привести к переполнению кучи и сообщению об ошибке:

203  Heap overflow error - ошибка переполнения кучи.

При объявлении указателя на значение любого типа ОП для самого значения не резервируется. Выделяется ОП только для переменной-указателя, но она при этом не имеет значения.

При этом случайное (неинициализированное) значение указателя ("мусор") может быть недопустимым значением адресов. Например, оно может совпасть с адресами размещения программы или данных пользователя или данных самой DOS. Запись значения по такому адресу может нарушить работоспособность программы пользователя или самой DOS. Компилятор не обнаруживает эту ошибку. Это должен делать программист.

2. Стандартные процедуры и функции для работы с динамической памятью

Подпрограммы управления кучей

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

Таблица 3.  Подпрограммы управления кучей

Заголовок подпрограммы 

Назначение подпрограммы 

Процедуры 

New (Var P : Pointer); 

Выделяет ОП в размере, определенном типом переменной, которую адресует Р 

Dispose (Var P : Pointer); 

Освобождает ОП, которую адресует Р 

GetMem (Var P : Pointer, Size : Word); 

Выделяет Size байт из кучи, ее адрес в Р - бестиповом указателе 

FreeMem (Var P : Pointer, Size : Word); 

Освобождает Size байт ОП, выделенной переменной, которую адресует Р 

Mark (Var P : Pointer); 

Запоминает в Р состояние кучи 

Release (Var P : Pointer); 

Восстанавливает состояние кучи 

GetImage (X1,Y1,X2,Y2: Integer; var P); 

Помещает в ОП Р^ копию фрагмента изображения экрана с координатами X1, Yl, X2, Y2 

PutImage (X,Y: Integer; var P; vid: Word); 

Выводит фрагмент изображения из Р^ в прямоугольник с координатами X, Y верхнего левого угла 

Функции возвращают: 

ImageSize (xl, yl, x2, y2: Integer): Word; 

количество байт ОП, необходимое для запоминания прямоугольного фрагмента экрана ограниченного xl, yl, х2, у2 

MaxAvail : LongInt; 

длину максимального свободного участка кучи 

MemAvail : Longlnt; 

сумму длин свободных участков кучи 

Addr(X): pointer; 

адрес переменной X 

Назначение параметров vid процедуры PutImage:

  1.  NormalPut  =  0; - замена изображения на копию;
  2.  для кода цветов изображения:
    XorPut =  1 - исключающее ИЛИ;
    OrPut =  2 - объединяющее ИЛИ;
    AndPut =  3 - логическое И;
    NotPut =  4; - инверсия кода.

Выделение оперативной памяти из кучи

Динамическая память может быть выделена с помощью процедур New или GetMem. При выделении ОП процедура New выделяет ОП, размер которой определяется типом указателя - фактического параметра, и присваивает указателю адрес начала выделенной ОП. Начиная с Паскаля версии 5 в процедуре New допускается второй параметр для выделения ОП переменной типа объект. GetMem выделяет ОП размера, заданного параметром Size (см. табл. 3).

За одно обращение к подпрограммам выделения ОП может быть выделено не более 65528 байт (64 Кбайт - 8 = 65536 - 8).

Если свободной ОП кучи недостаточно для выделения, возникает ошибка на этапе выполнения программы. Возможность возникновения ошибки можно определить с помощью функций без параметров MaxAvail или MemAvail -определения имеющейся свободной ОП кучи.

Для определения возможности размещения очередной динамической переменной надо сравнить значение результата функции MaxAvail с требуемым объемом ОП. Программа с анализом состояния кучи приведена в листинге 5.

Листинг . Анализ состояния кучи.

Type zap = record  FIO : string[20];

ZPL : real; end;

Var   P : pointer;

Begin {Анализ  наличия  требуемой  свободной ОП:   }

If MaxAvail  <  SizeOf(zap) then

Begin Writeln('Памяти  недостаточно');   Exit  End;

GetMem( P, SizeOf(zap) }; { - запрос  ОП для  Р^}

Writeln   ('Доступно   ',   MemAvail, ' байт');

Writeln   ('Наибольший  свободный  участок =   ',

           MaxAvaii,    '   байт');

FreeMem (P,SizeOf(zap));{ - освобождение ОП  Р^ }

End.

При запросе ОП кучи возможны следующие варианты:

1) куча пуста, например в начале выполнения программы или после ее освобождения; при этом монитор кучи заполняет кучу последовательно в порядке поступления запросов;

2) куча фрагментирована, т. е. в процессе работы образовались освобожденные участки ОП (дырки); монитор просматривает список свободных областей ОП и ищет такой свободный участок ОП, чтобы разница между размером свободного и требуемого участка ОП была минимальной.

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

203 Heap overflow error - ошибка переполнения кучи.

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

Освобождение оперативной памяти кучи

Динамическая ОП может быть освобождена следующими способами:

  1.  по завершении выполнения программы;
  2.  с помощью процедур:

а) Dispose, если ОП выделена процедурой New;;

б) FreeMem, если ОП выделена процедурой GetMem;;

3) с помощью процедур Mark и Release:
Mark(P);   ...   Release   (P);.

Примеры вызова процедур освобождения ОП:

Dispose   (P);   FreeMem(P,   Size);

Процедура Mark(P); запоминает состояние динамической области в переменной-указателе Р. Процедура Release освобождает всю динамическую ОП, выделенную с помощью New и GetMem, после выполнения процедуры Mark. После обращения к Mark и до оператора Release нельзя использовать обращения к процедурам Dispose и FreeMem. Например:

Var      P : POINTER; { - бестиповый указатель }

P1, P2, Р3 : INTEGER; { - указатели на тип INTEGER}

BEGIN   New ( P1 );

   Mark ( P );

New ( P2 ) ;  New ( P3 ) ; . . ..

Release (P); {- память для Р2^ и Р3^ освобождена; Р1^ - можно и далее использовать }

Подпрограммы для работы с графикой

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

Для работы с графикой используются процедуры (см. табл.3):

GetImage - для запоминания в ОП кучи копии прямоугольного участка экрана;

PutImage - для вывода на экран запомненного изображения в прямоугольник, заданный координатами его верхнего левого угла.

Для запоминания копии изображения предварительно должен быть выделен требуемый объем ОП с помощью одной из процедур.

В листинге 6 приведена программа для запоминания и перемещения прямоугольника по экрану в точки с координатами X, Y верхнего левого угла.

Листинг 6. Запоминание и перемещение прямоугольника по экрану.

PROGM  RIS; Uses   Graph,   Crt;

      Var  graph_drv,   graph_mode,

      i,j,dx,dy,x,у : integer; P   :   pointer;

Const N = 10;{- количество  точек для  отображения  прямоугольника }

Begin
graph_drv := detect; { -  для  автоматического  определения драйвера }

{ Инициализация  графического режима: } InitGraph(graph_drv,graph_mode,'D:\BP\BGI');

{ Анализ  успешности инициализации  графического режима:} If  GraphResult  <>  grOK  then

Begin Writeln   ('Неуспех!!');   Halt(1);   End;

SetFillStyle (1,2);{   -   стиль  и  цвет  заполнения   }

Bar(10,30,40,60); {  -  рисование  прямоугольника   }

{   Запрос  ОП  для  прямоугольника:    }

GetMem(P, ImageSize(10,30,40,60)); {   Запоминание   в  буфере  битового  образа  прямоугольника:    }

Getlmage(10,30,40,60,Р^);

SetBkColor(0); ClearDevice; { - окраска экрана черным цветом }

DX := Round ( GetMaxX / N );    X := 0;

DY := Round ( GetMaxY / N );    Y := 0;

For I := 1 To  N Do Begin

Putlmage(X, Y, РЛ, 0); { - вывод изображения на экран }

Delay (200); { - задержка выполнения программы }

PutImage(X,Y, Р^, 1); { - стирание изображения }

X := X + DX;  Y := Y + DY; { - изменение координат  }

End;                          {   изображения на экране}

CloseGraph;

END.

Для рисования изображений на экране можно использовать процедуры построения фигур:

  1.  из точек и линий: PutPixel,  DrawPoly,  Line,  LineRel, LineTo;
  2.  из линий:

Bar - закрашенный прямоугольник;

Bar3D - закрашенный параллелепипед;

RectAngle - прямоугольник;

3) криволинейных:

Arc - дуга окружности;

PieSlice - закрашенный сектор круга;

Ellipse - дуга эллипса;

Sector - закрашенный сектор эллипса.

3. Указатель - результат выполнения полпрограммы

Результат подпрограммы - указатель на арифметическое значение

Возвращаемым результатом работы подпрограммы, процедуры или функции, может быть любое скалярное значение, в том числе скалярный указатель на значение любого типа, простого или сложного. Рассмотрим результат подпрограммы - указатель на арифметическое значение, найденное в массиве.

В листинге 7 приведена программа с функцией POISK для поиска заданного значения в одномерном массиве. Результатом работы функции является указатель на значение, найденное в массиве А. Для массива А в программе листинга 7 память выделена статически. Функция поиска может возвратить указатель на данные массива, которому ОП выделена как статически, так и динамически.

Листинг 7. Поиск заданного значения в одномерном массиве чисел и возврат из фуниии поиска указателя на найденное значение.

CONST  А : ARRAY[1..5] OF  BYTE = (1,2,3,4,5);

TYPE PBYTE = ^BYTE; { -  имя  типа - указатель на BYTE}

VAR  P : PBYTE; {   -  переменная  типа   PBYTE   }

{        _   ФУНКЦИЯ  ПОИСКА    }

FUNCTION POISK (VAR В : ARRAY OF BYTE; К : BYTE):PBYTE;

VAR I: BYTE; {В - открытый массив; К - искомое значение}

BEGIN  FOR I := LOW(В) TO HIGH (В) DO {Успешный поиск: }

IF В[I]=К THEN BEGIN  POISK := @ В[I]; EXIT END;

POISK   := NIL; {   -  при  неуспешном поиске   }

END;

{ Основная программа    }

BEGIN P := POISK (A,4) ; { -  вызов  функции  поиска }

IF  P = NIL  THEN  WRITELN  (  'ПОИСК  НЕУСПЕШЕН')

ELSE WRITELN ('НАЙДЕНО ЗНАЧЕНИЕ A[I]= ', Р^); END.

В листинге 8 приведена программа с процедурой POISK, возвращающей указатель на значение типа BYTE с помощью параметра-переменной.

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

CONST А : ARRAY [1..5] OF BYTE  = (1,2,3,4,5);

TYPE  PBYTE = ^BYTE; {-  имя  типа - указатель на BYTE }

VAR P : PBYTE; { - переменная типа PBYTE }

{   ПРОЦЕДУРА ПОИСКА - - }

PROCEDURE POISK (VAR В:ARRAY OF BYTE;К:BYTE; VAR UK:PBYTE); VAR I : BYTE; { В - открытый массив; К - искомое значение } BEGIN

FOR I := LOW(В) TO HIGH(В) DO         { Успешный поиск: } IF В[I] = К THEN BEGIN UK := @В [I]; EXIT END;

UK := NIL;  { - при неуспешном поиске }

END;

{  Основная программа       - }

BEGIN   POISK ( A, 4, P ); { - вызов процедуры поиска }

IF P = NIL THEN WRITELN ('ПОИСК НЕУСПЕШЕН')

ELSE WRITELN ('НАЙДЕНО ЗНАЧЕНИЕ A[I] = ', Р^); END.

Результат подпрограммы - указатель на запись

Результат - указатель на запись рассмотрим на примерах.

В листинге 9 представлена программа с процедурой для поиска данных в массиве записей, возвращающей в качестве результата UK-указатель на элемент массива записей.

Листинг 9. Поиск заданного значения в одномерном массиве записей и возврат из процедуры поиска указателя на найденную запись.

TYPE  S =RECORD {  -  имя  типа  записи   }

    NZ : INTEGER; {   -  номер  зачетки       }

   FIO : STRING[20] {   -  Ф.И.О.   студента   }

END;

 PS = ^S; { -  имя   типа  -  указателя  на  тип   S }

CONST   STUD : ARRAY [1..2] OF S= {   -  инициализация   }

((NZ :123; FIO: 'ИВАНОВ'), {   массива  записей   }

( NZ :222; FIO: 'ПЕТРОВ'));
VAR  P: PS; {- переменная  -  указатель   на  тип   S   }

{       ПРОЦЕДУРА  ПОИСКА  -     }

PROCEDURE POISK1(VAR В:ARRAY OF S; N:INTEGER; VAR UK:PS);
VAR I:INTEGER;{В - открытый массив; N - искомое значение}

BEGIN FOR I:=LOW(В) TO HIGH(В) DO {  Успешный  поиск:  }

IF  B[I].NZ   =   N   THEN  BEGIN UK:= @В[I];   EXIT   END;

UK   := NIL; {   -  при  неуспешном поиске   }

END;

{      Основная  программа      }

BEGIN  POISK1 (STUD,222,P);{- вызов  процедуры поиска }

IF   P = NIL   THEN   WRITELN    ('ПОИСК  НЕУСПЕШЕН')

ELSE WRITELN ('НАЙДЕНА  ЗАПИСЬ:', P^.NZ,' ',Р^.FIO   ); END.

В листинге 10 представлена программа с функцией для поиска данных в массиве записей, возвращающая в качестве результата указатель на элемент массива записей.

Листинг 10. Поиск заданного значения в одномерном массиве записей и возврат из функции поиска указателя на найденную запись.

{ Объявления те же,что и в примере листинга 9}

{   -  ФУНКЦИЯ   ПОИСКА   - -   }

FUNCTION POISK2  (VAR В:ARRAY OF S;  N:INTEGER):PS;

VAR I : INTEGER; {В - открытый массив; N – искомое значение}

BEGIN FOR I := LOW(В) TO HIGH(В) DO

IF B[I].NZ = N THEN BEGIN POISK2 := @ B[I]; EXIT END;
POISK2  := NIL; { -
при неуспешном поиске }

END;

{   Основная программа   }

BEGIN  P:=POISK2 (STUD,222); { - Вызов функции поиска } IF Р = NIL THEN WRITELN ('ПОИСК НЕУСПЕШЕН')

   ELSE WRITELN ('НАЙДЕНА ЗАПИСЬ:',P^.NZ,' ',Р^.FIO); END.

4. Указатели и динамические массивы

Указатель на одномерный динамический массив

Работа с одномерным динамическим массивом арифметических значений

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

В листинге 11 представлена программа для формирования динамического одномерного массива данных типа REAL: выделения ему ОП в основной программе, ввода в него данных из текстового файла и вывода данных массива с помощью процедуры Р. Количество элементов динамического массива объявлено равным единице, но его можно объявить максимально возможным (с ОП не более 64 Кбайт), а запросить и использовать ОП только для N элементов массива.

Листинг 11. Формирование одномерного динамического массива вещественных значений в основной программе.

PROGRAM  PR16;  {$r-} {-  отключение контроля границ массива }

TYPE ТА = ARRAY [1..1] OF REAL;{ -   тип  массива   }

РА =   ^ТА; {   -   тип указателя  на массив   }

VAR А :РА;{   -  динамическая  переменная   }

N,I,J : INTEGER;  FI : TEXT.;

{   - ПРОЦЕДУРА ВЫВОДА - - }

PROCEDURE Р(В : РА;  N : INTEGER); BEGIN  FOR I := 1 ТО  N DO

WRITE ( B^[I]:5:0);      { - вывод значения } END;

{           Основная программа   }

BEGIN

ASSIGN (FI, 'LR5.DAT');  RESET ( FI );

{Ввод и вывод N - количества элементов массива:} READLN (FI, N ); WRITELN ( #10#13, ' N= ', N:3);

GETMEM (A, N * SIZEOF(REAL)); { - запрос ОП для А^ }

FOR I:=1 ТО N DO READ (FI,А^[1]); { - ввод значений }

Р ( А, N ) ; { - вызов процедуры вывода }

FREEMEM (А,N * SIZEOF(REAL));  { - освобождение ОП А^}

А := NIL; { - обнуление указателя }

CLOSE    (FI); END.

Исходные данные:

5  -  количество  элементов массива

2   3   4   б   7   8   -   значения  его  элементов.

В листинге 12 представлен фрагмент программы, использующей процедуру для выделения ОП динамическому одномерному массиву и его заполнения исходными данными. Процедура возвращает указатель на динамический одномерный массив через список параметров.

Листинг 12. Формирование одномерного динамического массива вещественных значений в процедуре, возвращающей указатель на сформированный массив.

PROGRAM LR5;  {$r-}

{Объявления те же, что и в примере листинга 11 -}

{ПРОЦЕДУРА ВЫВОДА  та же, что и в примере листинга 11}

{ПРОЦЕДУРА ЗАПРОСА ОП И ЗАПОЛНЕНИЯ МАССИВА - - }

PROCEDURE VVOD ( VAR В : РА );{ - В - параметр-переменная }

ASSIGN (FI, 'LR5.DAT');  RESET ( FI ); READLN (FI, N ); WRITELN ( #10#13, ' N= ', N:3); BEGIN

GETMEM ( B,  N * SIZEOF (REAL) ); { - запрос ОП для В^ }

FOR I := 1 TO N DO  READ ( FI, BA[I] );{- ввод значений В^}

CLOSE (FI); END;

{   Основная программа   - }

BEGIN

VVOD ( A ); { - вызов процедуры ввода данных }

Р ( А, N ); { - вызов процедуры вывода значений }

FREEMEM ( А, N * SIZEOF (REAL) ); { - освобождение ОП А^  }

А := NIL; { - обнуление указателя }

END.

В листинге 13 представлен фрагмент программы, использующей функцию VVOD для выделения ОП одномерному динамическому массиву данных и заполнения его исходными данными. Функция VVOD возвращает указатель на одномерный динамический массив.

Листинг 13. Формирование одномерного динамического массива вещественных значений в функции, возвращающей указатель на сформированный массив.

PROGRAM LR5;  {$г-}

{ Объявления те же, что и в примере листинга 11 }

{ ПРОЦЕДУРА ВЫВОДА - та же, что и в примере листинга 11}

{       ФУНКЦИЯ ЗАПРОСА ОП И ЗАПОЛНЕНИЯ МАССИВА - - }

FUNCTION VVOD : PA;   VAR В: РА;

ASSIGN (FI, 'LR5.DAT');  RESET ( FI ); READLN (FI, N ); WRITELN ( #10#13, ' N= ', N:3);

BEGIN   GETMEM ( B,  N * SlZEOF ( REAL ) );        { - запрос ОП для Вл }

FOR I:=1 TO N DO  READ (FI, В^[1] ); { - ввод значений В^}

VVOD := В; { - возврат значения указателя }

CLOSE (FI); END;

{  - Основная программа  --}

BEGIN

А := VVOD; { - вызов функции ввода данных }

Р ( А, N ); { - вызов процедуры вывода значений }

FREEMEM ( А, N * SIZEOF (REAL) ) ;{ - освобождение ОП А^}

А := NIL; { - обнуление указателя }

END.

Работа с многомерным динамическим массивом арифметических значений

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

В листинге 14 представлена программа, использующая процедуру для выделения ОП двумерному массиву и его заполнения исходными данными в основной программе. Обращение к A[I,J] - элементам двумерного массива производится с помощью индексов - выражений, определяющих положение элемента в одномерном массиве такого же размера:

(I-1)*N+J,

где     N - количество элементов в строке;

          I - номер требуемой строки;

          J - номер элемента в требуемой строке;

(I-1)*  N - количество элементов в полных строках, расположенных до требуемой строки;

(I  -  1)   *  N + J - номер элемента двумерного массива в одномерном динамическом массиве.

Листинг 14. Формирование двумерного динамического массива вещественных значений в основной программе с использованием указателя на одномерный массив.

PROGRAM  LR5;

TYPE ТА = ARRAY [1 ..100 ] OF REAL; { - тип массива }

PA = ^ТА; { - тип указателя на массив }

VAR    А : РА; { - динамическая переменная }

N, I, J : INTEGER; FI : TEXT;

{   -— ПРОЦЕДУРА ВЫВОДА }

PROCEDURE Р(В : РА;  М, N : INTEGER); BEGIN  FOR I := 1 ТО  M DO    BEGIN

FOR J := 1 TO N DO { Вывод значения:      }

WRITE ( В^[ (I -1) * N + J ] : 7 : 2 );
WRITELN; END;

END;

{          - Основная программа }

BEGIN

ASSIGN (FI, 'LR52.DAT');  RESET ( FI );

READLN (FI,M,N ); {- ввод количества строк и столбцов: } WRITELN ( #10#13, 'М = ', М : 3, ' N= ', N : 3);

GETMEM (А,М * N *SIZEOF (REAL));{ - запрос ОП для А^} FOR I := 1 ТО М DO

FOR J := 1 ТО  N  DO

READ (FI,А^[(1-1)* N+ J]);{ - ввод значений А^}

Р (А,М, N) ; FREEMEM ( А, М * N * SIZEOF ( REAL));       {   -   освобождение   ОП  А^ }

А := NIL; { - обнуление указателя }

END.

Исходные данные:

23- количество строк и столбцов массива 1.2  3   4.5 - значения его элементов 3 6.1 7.8

Схематично для N = 3 взаимосвязь элементов одномерного и двумерного массивов представлена на рис. 6.

Рис.   6

Схематичное представление взаимного расположения элементов двумерного и соответствующего ему одномерного массивов

Использование процедуры и функции для выделения ОП и заполнения двумерного динамического массива аналогично рассмотренному в примерах листингов 12 и 13.

Работа с одномерным динамическим массивом записей

Для формирования динамического массива записей используется указатель на массив записей. Пример программы для формирования динамического массива записей дан в листинге 15. В программе используются:

MSTRUC - имя типа - массива записей со сведениями о студентах; PMSTRUC - имя типа - указателя на массив записей;

Z - переменная - указатель на массив записей.

Листинг 15. Формирование одномерного динамического массива записей в основной программе.

PROGRAM  LR6;    USES CRT; { Имена  типов:    }

TYPE ST  = ARRAY [1..7] OF  STRING[55]; {- массива записей }

ST1     =   STRING[12];

STRUG  =  RECORD { -   записи  в   составе:      }

NZ : 96100.. 96200;{ -  номер  зачетки          }

GR   :   STRING[6];{ -   группа                            }

FIO   :   ST1; { -  фамилия                         }

RS : REAL; { - размер стипендии }

END;

MSTRUC = ARRAY [ 1 .. 10 ] OF STRUG;{ - массива записей  }

PMSTRUC = ^МSТРUС;  { - указателя на массив записей }

CONST С : ST = ( { - шапка таблицы }

СВЕДЕНИЯ О СТУДЕНТАХ ' ,

'!   НОМЕР ! ГРУППА ! ФАМИЛИЯ,     ! РАЗМЕР      1',

' !  ЗАЧЕТНОЙ   КНИЖКИ  ', ',   ИНИЦИАЛЫ ! СТИПЕНДИИ   ! ' ,

' !=================+========+=========:====+===========,

VAR   Z : PMSTRUC; { - указатель на массив записей }

N : INTEGER; { - количество записей         }

I, J : 1 ..60;   В : CHAR;
FI, FR : TEXT; { - текстовые файлы }

{   Вывод одной строки таблицы   }

PROCEDURE Р; BEGIN

WRITELN (  FR, '!', Z^[I].NZ : 10, '!':8,' ', Z^[I]-GR, '|':2,  ' ', Z^[I].FIO, '!', Z^[I].RS : 9 : 2, '! ' :4 );

IF I = N THEN WRITELN ( FR, С[7] )

ELSE WRITELN ( FR, C[6] );

END;

{   Основная программа   }

BEGIN    CLRSCR;

ASSIGN ( FI, 'LR6.DAT' );  RESET ( FI );

ASSIGN ( FR, 'LR6.RES' );  REWRITE ( FR ) ;

FOR I:=1 TO 5 DO WRITELN (FR,С[I]); {- вывод шапки таблицы}

READLN(FI,N}; WRITELN('N = ',N:5); { - ввод колич. записей}

GETMEM (Z, N*SIZEOF(STRUG); {-запрос ОП для Z^ - N записей}

FOR I := 1 TO N   DO { Ввод и вывод массива записей: }

WITH  Z^[I]  DO BEGIN       { - присоединение к I-записи  }

READLN(FI,NZ,B,GR,B,FIO,RS); P; { - ввод и вывод I-записи }

END; CLOSE (FI);      CLOSE (FR);

FREEMEM (Z,N*SIZEOF(STRUG); Z:=NIL;{- освобождение ОП Z^}

END.

Исходные данные для формирования массива записей:

5 - количество записей массива;

88103 ЭВМ1-1 ИВАНОВ И.И.   4000.

88150 ЭВМ1-2 ПЕТРОВ П.П.   5000

. . .

88200 ЭВМ1-3 СИДОРОВ С.С. 7055.6

Указатели на многомерные массивы

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

В листинге 16 представлена программа для работы с двумерным динамическим массивом. Запрос ОП и ввод данных производится в основной программе. Исходные данные те же, что и в листинге 14.

Листинг 16. Формирование двумерного динамического массива вещественных значений в основной программе с использованием указателя на двумерный массив.

CONST  N = 3; { - количество столбцов матрицы }

TYPE  ТА = ARRAY [ 1..20, 1..N] OF REAL; { - тип массива }
РА = ^ТА; { - тип указателя на массив }

VAR  А : РА; { - динамическая переменная }

М, I, J : INTEGER;   FI : TEXT;

{   ПРОЦЕДУРА ВЫВОДА   - }

PROCEDURE P(B : РА;  М, N : INTEGER); BEGIN  FOR I := 1 ТО  M DO    BEGIN FOR J := 1 TO N DO

WRITE ( В^ [I,J]:7:2 );     { - вывод значения }
WRITELN; END;

END;

{  Основная программа  }

BEGIN    ASSIGN (FI, 'MAS2MERN.DAT');  RESET ( FI );
READLN (FI, M ); { - ввод количества строк матрицы }

WRITELN ( #10#13, 'М = ', М : 3, ' N= ', N : 3);

GETMEM (А,  М * N * SIZEOF ( R ) ); { - запрос ОП для А^ } FOR I := 1 ТО М DO

FOR J := 1 ТО  N  DO

READ ( FI, А^ [I, J] ) ; { - ввод значений А^ }

P ( A, M, N); { - вызов процедуры вывода значений }

FREEMEM (А,М*N*SIZEOF (R)); { - освобождение ОП А^ }

А := NIL; { - обнуление указателя }

END.

Исходные данные:

2

1.2     3        4.5

3        6.1     7.8


 

А также другие работы, которые могут Вас заинтересовать

45028. Понятие языка программирования. Классификация языков программирования 29.44 KB
  Классификация языков программирования. И такое средство было найдено: различные символические языки и соответствующие им трансляторы системы программирования. Также система программирования может включать в себя: библиотеки стандартных подпрограмм отладчик компоновщик и другие сервисные средства.
45029. Интерфейс и основные приемы работы c САКК Magister-2000 456 KB
  Задание может содержать как текстовые фрагменты так и объекты статической графики поддерживаемые rtfформатом; задания могут включать следующие типы анализаторов ответа учащегося: выбор одного верного ответа из нескольких предложенных выбор нескольких верных из предложенных слово последовательность символов; задания на установление соответствия двух списков; задания на установления последовательностей действий или событий; задания сохраняются во внутреннем формате исключающем возможность их просмотра с целью выяснения верных...
45030. Интонация как единица фонетического уровня языка 17.03 KB
  Повышения тона вверх вниз называются интонацией. В русском языке можно выделить 6 интонационных конструкций. Основными различительными признаками ИК является направление движения тона в центре и уровень тона в постцентровой части.
45031. Устройства ввода информации 20.71 KB
  Клавиатура устройство представляющее собой набор кнопок клавиш предназначенных для управления каким-либо устройством или для ввода информации. Трекбол указательное устройство ввода информации об относительном перемещении для компьютера. Сканер изображений устройство для считывания двухмерного плоского изображения и представления его в растровой электронной форме. Графи́ческий планше́т это устройство для ввода рисунков от руки непосредственно в компьютер.
45032. Путешествие по Индии 128 KB
  Супер Нам всё нравится 20 Отели Надо отметить что в Индии ни на одной отельной вывеске вы не увидите заветных звезд. Стандартный набор осматриваемых объектов в столице это Ворота Индии Здание Высокого суда Старый Форт и знаменитая мечеть Кутуб Минар с которой и начинается наша экскурсия. И тут перед нами предстала картина которую возможно увидеть пожалуй только в Индии.
45033. Семантика по книге Стивена Пинкера «Язык как инстинкт» 130 KB
  Пинкер известен за его широко охватывающую защиту эволюционной психологии и Вычислительной теории разума. Академическая специализация Пинкера визуальное восприятие и развитие речи у детей и он более известен как популяризатор идеи о том что язык на котором мы говорим является инстинктом или биологической адаптацией сформированной естественным отбором. Этот доклад был написан мною по одной из самых известных книг Стивена Пинкера Язык как инстинкт.
45034. Инженерная подготовка строительной площадки 42.64 KB
  Бетонную смесь готовят бетоносмесителями и транспортируют с помощью системы внутренних транспортных средств до места заливки либо привозят готовую бетонную смесь автобетоносмесителями или самосвалами Технология устройства защитных покрытии Гидро и пароизоляционные работы выполняют по завершению изготовления конструкции или монтажа сборных конструкций. Однако эти работы могут вестись параллельно с некоторым технологически обусловленным отставанием от работ по изготовлению конструкций на которые будет наноситься гидро и пароизоляция. В...
45035. Семантические принципы 29.5 KB
  Принцип предметности: предложение должно говорить о предметах обозначаемых входящими в него именами а не о самих этих именах. Предложение Стул - это существительное построено правильно. Принцип взаимозаменимости: при замене имен с одинаковым значением предложение в котором эта замена осуществляется не должно изменять свое истинностное значение истинное предложение должно оставаться истинным а ложное – ложным. Пусть дано предложение Земля вращается вокруг Солнца.
45036. TRAVELLING BY AIR 33.95 KB
  Modern life is impossible without traveling. There are many ways of traveling: by sea, by plane, by train, by car, on foot. Tastes differ. That іs why it is up to you to decide which means of travelling you'd prefer