34670

Организация памяти. Статические и динамические структуры данных

Реферат

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

Статические и динамические структуры данных Организация памяти. Статические и динамические структуры данных [1] Статическая память [2] Автоматическая память [3] Динамическая память [4] Распределение оперативной памяти [4.3] Управление блоками динамической памяти Как бы ни была велика основная память современных ЭВМ программистам её всегда не хватает.

Русский

2013-09-08

182 KB

29 чел.

исциплина «Основы алгоритмизации и программирование»  Организация памяти. Статические и динамические структуры данных

Организация памяти. Статические и динамические структуры данных

[1] Статическая память

[2] Автоматическая память

[3] Динамическая память

[4] Распределение оперативной памяти

[4.1] Процедуры New и Dispose

[4.2] Процедуры GetMem и FreeMem

[4.3] Управление блоками динамической памяти

Как бы ни была велика основная память современных ЭВМ, программистам её всегда не хватает. В этой связи актуальна задача эффективного её использования. Предоставляем несколько механизмов управления памятью.

Статическая память

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

В языке Turbo Pascal нет явного упоминания о статической памяти, но фактически к ней можно отнести память, в которой размещаются константы и переменные, которые описаны на уровне главной программы. Пример программы с использованием статической памяти.

program Static;

var i: Integer;

      procedure Sub;

      var Cnt:integer;

     begin inc(Cnt); WriteLn('Cnt = ',Cnt) end;

begin

for i:=1 to 3 do Sub;

end.

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

Автоматическая память

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

Для примера рассмотрим два варианта построения программы решения одной и той же задачи: Найти произведение сумм элементов массива.


program Prgm1;

const m = 10000;

var a: array[1..m] of Integer;

   b: array[1..m] of Real;

   i,Sa: Integer;

   Sb:   Real;

begin

{ Ввод массивов a и b }

Sa:=0; Sb:=0;

for i:=1 to m do Sa:=Sa+a[i];

for i:=1 to m do Sb:=Sb+b[i];

WriteLn(Sb*Sa);

end.

program Prgm2;

const m = 10000;

var Sa: Integer;

   Sb: Real;

procedure SumA;

var a: array[1..m] of Integer;

   i: Integer;

begin

{ Ввод массива a }

for i:=1 to m do Sa:=Sa+a[i];

end;

procedure SumB;

var b: array[1..m] of Real;

   i: Integer;

begin

{ Ввод массива b }

for i:=1 to m do Sb:=Sb+b[i];

end;

begin

Sa:=0; Sb:=0;

SumA; SumB;

WriteLn(Sb*Sa);

end.

Не смотря на то, что это абстрактные программы, они обе формально правильные и можно попытаться их выполнить. При попытке выполнить первую программу мы получим сообщение об ошибке компиляции "Error 96". Причина ошибки заключается в том, что общий объём памяти, который выделяется переменным уровня главной программы не должен превышать 64 KB. Учитывая, что переменная типа Integer занимает 2 байта, а типа Real - 6 байтов, то для нашей программы мы получим оценку 10000*(6+2) B = 80000 B = (80000 / 1024) KB = 78.2 KB, что превышает имеющиеся возможности.

Попытка выполнить вторую программу тоже приводит к сообщению об ошибке компиляции - "Error 202" (переполнение стека). Так как массивы находятся в разных процедурных блоках программы, а они вызываются последовательно, то это означает, что массивы последовательно используют одну и ту же память в стеке. Оценка необходимого объёма памяти в этом случае составляет 6*10000 B = 58.6 KB - размер большего массива. Причина ошибки состоит в том, что по умолчанию размер стека составляет всего 16 KB, но, к счастью, его можно увеличить до значения 65 520 B, что составляет почти 64 KB. Для этого в интегрированной среде надо выбрать Options | Memory sizes и установить желаемый размер стека. После этого вторая программа будет выполняться без ошибок.

Динамическая память

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

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

Указателю можно присваивать значение указателя того же типа, константу NIL (пустой указатель) или адрес объекта, определенный с помощью стандартных функций модуля System - Addr (ИмяПеременной) (оператора @ИмяПереиенной) и Ptr(СегментнаяЧастьАдреса, Смещение).

Сегмент данных — это непрерывная область оперативной памяти с объемом в 65 536 байт (64 Кбайт).

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

Указанное свойство таких переменных весьма полезно при обработке крупных массивов данных. Область памяти, в которой размещаются динамические переменные, называется кучей (heap), максимальный ее объем более чем в 6 раз превышает объем сегмента данных и составляет около 400 Кбайт.

Это не означает, что отдельные переменные (запись или массив) могут иметь такой большой размер. Конечно же, нет: ни одна переменная в Turbo Pascal не может превышать объем 65 520 байт. Однако распределить составляющие записи или массива по различным сегментам памяти оказывается вполне возможным. При этом общий объем «распределенной» переменной может существенно превысить объем отдельного сегмента. Распределение переменной по разным сегментам кучи особенно полезно, когда ее объем заранее предсказать нельзя.

Рекомендуется применять динамические переменные если:

  1.  Необходимы переменные, имеющие большой объем и освобождающие память после их использования.
  2.  Размер переменной трудно предсказуем.
  3.  Размер переменной превышает 64 Кбайт.

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

Объем оперативной памяти в Turbo Pascal составляет около 640 Кбайт. Эта память делится на сегменты, каждый из которых не превышает 64 Кбайт. В отдельном сегменте размещается код главной программы. Если к программе подключены модули, то по одному такому сегменту выделяется и для каждого модуля. Кроме этого, сегмент памяти обязательно выделяется под стандартный модуль System, автоматически подключаемый к каждой программе. Все перечисленные сегменты носят название сегментов кода, поскольку в них содержатся коды программы или ее модулей.

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

В сегменте стека хранятся локальные переменные программы и переменные исполнительных частей ее модулей в периоды работы подпрограмм, где эти переменные описаны.

Размеры всех сегментов, кроме сегмента стека, определяются в процессе компиляции и во время работы программы не меняются. Размер сегмента стека, как и размер любого другого сегмента, не может превышать 64 Кбайт, но по умолчанию (в случае отсутствия специальной директивы компилятора) его объем устанавливается равным 16 Кбайт.

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

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

{$М «Размер стека», «Минимальный объем динамической памяти», «Максимальный объем динамической памяти»}

Эту директиву применяют в теле программы (после слова BEGIN) перед использованием соответствующих переменных, память в ней измеряется в байтах. Конкретный пример использования такой директивы:

{$М1024,1000 200000}

На рис. 1 приведена компактная схема распределения оперативной памяти, или карта памяти, Turbo Pascal. В этой схеме указанная последовательность сегментов следует снизу вверх. Это достаточно удобно и оправдывает использование синонима «куча» для динамической памяти, которая располагается на схеме в самом верху и, как всякая куча, заполняется снизу вверх.

Рис. 1. Схема распределения оперативной памяти

Согласно этой схеме при отсутствии стандартных и нестандартных модулей максимальный объем динамической памяти, грубо говоря, может составлять

640 - 64*3 - 16 = 432 Кбайт.

Таким образом, при работе с динамическими переменными вы можете рассчитывать на кучу с максимальным объемом около 400 Кбайт.

Как вы знаете, только переменные таких типов, как byte, char, shortInt и boolean, занимают во внутреннем представлении машины один байт (одну ячейку), для размещения переменных всех других простых типов требуется уже нескольких смежных ячеек. Структурированные переменные, например, такие как массивы и записи, могут занимать значительно большие объемы памяти.

Память под динамические переменные выделяется порциями по 8 байт. Поэтому, например, для переменных типа char и shortInt, имеющих размер в один байт, и для real, с объемом в 6 байт, будет выделено по 8 байт. Блоки памяти под переменные больших размеров (массивы, записи и др.) будут кратны 8.

Каждый байт памяти компьютера располагается в отдельной ячейке. Каждая же ячейка памяти имеет свой адрес, по которому к ней можно обратиться. Поэтому адрес часто еще называют указателем или ссылкой. Ясно, что и первые ячейки всех переменных также имеют свои адреса — ведь они ничем не хуже всех других. При этом адреса этих ячеек называют адресами соответствующих переменных. Следует помнить, что адреса всех переменных, как статических, так и динамических, располагаются в сегменте данных и поэтому принадлежат к статической памяти. Каждый такой адрес занимает 4 байта памяти и состоит из двух частей — двух шестнадцатиразрядных слов, которые называются сегментом и смещением. Сегмент — это адрес первой ячейки одного из сегментов памяти, объемом в 64 Кбайт. Смещение указывает число ячеек (или байт), на которое текущий адрес отличается от адреса первой ячейки требуемой переменной.

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

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

Стрелочками обозначены типизированные константы-указатели. Все они описаны в модуле System. Первоначально их значение равно Nil.

Nil— это пустая ссылка, то есть адрес ячейки в статической памяти, которая не связана ни с каким объектом в куче, и поэтому объем такой динамической переменной равен нулю. Далее мы подробно рассмотрим, каким образом в эти типизированные константы-указатели записываются адреса соответствующих ячеек кучи. Сейчас же лишь отметим, что при правильной организации работы программы с помощью этих указателей удобно осуществлять управление состоянием кучи. В принципе, можно для аналогичных целей придумать свои идентификаторы. Однако разумнее применять уже существующие, поскольку их объявление не требуется (они уже описаны в модуле System), основываются они на двух английских словах, подсказывающих смысл этих переменных. Заметим здесь, что ptr — сокращение служебного слова pointer (стрелка, указатель) — указатель без типа (нетипизированный указатель или ссылка); org — от origin — начало.

Рис. 2. Верхняя часть карты памяти

 

Процедуры New и Dispose

Процедура New(Var P: Pointer) отводит место для динамической переменной и присваивает ее адрес ссылке Р. Для того чтобы эта процедура выделила необходимое место для такой переменной, в разделе описания var, необходимо указать тип (фактический размер) переменной (стандартный или пользовательский). С этой целью слева от идентификатора типа ставится знак карата «^». Этот знак фактически указывает размер переменной, которую порождает процедура New. Последующее использование этой же процедуры позволит выделить место для другой динамической переменной, адрес первой ячейки которой будет сдвинут относительно адреса первой ячейки первоначальной переменной на то количество ячеек (байт), которые требуются для размещения первоначальной переменной.

Процедура Dispose(Var P:Pointer) уничтожает связь, созданную ранее процедурой New, между указателем Р и объектом, на который он указывает (to dispose — освобождать).Обычно глубокое понимание «устройства» динамических переменных и усвоение правил работы с ними приходит далеко не сразу, и поэтому к некоторым абзацам настоящего раздела вам придется возвращаться несколько раз.

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

Туре

 Okrygn_Zap= Record

 x:Integer; {Координаты центра окружности}

 y:Byte;

 Radius: Byte;

 End;

 Ykazat_Zap= ^Okrygn_Zap;

Var

 Ykazat_l:^Char;{Указатель на элемент типа Char}

 Ykazat_2:^Real;{Указатель на элемент типа Real}

 Ykazat_3:^Okrygn_Zap;{Указатель на элемент типа Okrygn_Zap}

BEGIN

 New(Ykazat_l);{Создали динамическую переменную}

 Ykazat_1^:= 'A';{Присваивание объекту конкретного значения}

 WriteLn('Литера',Ykazat_1^);{Литера А}

 Dispose(Ykazat_1); {Освобождает память, занятую переменной Ykazat_l}

END.

В разделе описания переменных место под динамические переменные еще не выделяется, а компилятору лишь сообщается информация о том, что в теле программы переменная заданного типа (размера) может быть выделена. В теле программы процедура New выделяет в куче необходимое количество смежных ячеек под заданную динамическую переменную (в примере это Ykazat_l) и адрес первой ячейки этого блока ячеек записывает в ячейках статической памяти, присваивая им имя динамической переменной. Таким образом, динамическая переменная состоит из двух частей: статической переменной-адреса (имя переменной) и объекта, расположенного в куче по указанному адресу.

Для обращения к такому объекту знак карата следует ставить после идентификатора динамической переменной. Процедура Dispose(Ykazat_l) возвращает состояние кучи, которое было в ней до использования процедуры New(Ykazat_l). Ячейки объекта переменной Ykazat_l могут теперь быть использованы для размещения в куче других объявленных переменных: Ykazat_2, Ykazat_3.

Очень наглядно обе части динамической переменной представлять в виде прямоугольников, соединенных стрелкой. Такая модель динамической переменной приведена на рис. 3.

В первом прямоугольнике «хранится» адрес, а во втором — объект динамической переменной. Стрелка как бы связывает конкретные ячейки статической памяти, в которых указан адрес объекта, с ячейками памяти в куче, выделенные под объект по указанному адресу.

Рис. 3. Модель динамической переменной

Прокомментируем работу следующего фрагмента программы.

Var

 A,B:^Integer;

 {B программе могут быть использованы динамические переменные типа Integer}

BEGIN

 New(A); New(B);

   {Динамические переменные А и В порождены: места под объекты А^ и В^

    в куче выделены, а их адреса записаны в переменных А и В. Сами же объекты

    еще пусты, в них ничего не записано}

 А^:= 19; В^:= 49;

   {В объекты А^ и В^ динамических переменных А и В записали числа

    соответственно 19 и 49}

 А:= В;

   {В динамических переменных А и В записан адрес одного и того же объекта,

    в котором расположено число 49. Адреса объектов обеих переменных совпадают.

    Объект с числом 19 теперь является недоступным для программиста}

 В:= Nil;

   {Динамическая переменная В не связана с каким-либо объектом}

END.

Если указатели однотипны, как в вышеприведенном примере переменные А и В, то их значения можно присваивать друг другу (А:= В;). Для таких указателей допустимы операции сравнения (А> В).

Если же типы указателей различны, то присваивать их значения друг другу нельзя. Это ограничение реализовано в синтаксисе языка: за его выполнением строго следит компилятор. Поэтому, если даже размеры разных типов одинаковы (например, byte и char), то компилятор все равно взаимные присваивания между ними не позволит.

Однако в Turbo Pascal имеется специальный механизм, который позволяет вес же обойти это ограничение. Он основывается на использовании нетипизированных указателей pointer. Такие указатели объявляются стандартным образом:

Var

 х: Pointer;

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

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

if B= Nil then B:= New(A);

Ранее отмечалось, что процедура Dispose возвращает состояние кучи, которое было в ней до использования процедуры New, и ячейки, ранее занятые под уничтоженную переменную, могут быть использованы для размещения в куче любых других объявленных переменных. Однако это утверждение не совсем верно. Дело в том, что в освободившемся блоке ячеек могут быть размещены только объекты тех переменных, размеры которых либо равны числу освободившихся ячеек, либо меньше этого числа. В первом случае куча заполняется информацией снизу вверх без пропусков ячеек памяти. Во втором случае, когда размер освободившегося блока памяти больше размера объекта новой динамической переменной, блок ячеек заполняется лишь частично. Конечно, он может быть дополнительно заполнен введением последующей динамической переменной, если ее размер не будет превышать этот остаток. Однако во всех случаях, когда под новую переменную необходим больший блок ячеек, такой блок выделяется сразу же за блоком памяти объекта последней динамической переменной, если даже он совсем пуст или же заполнен лишь частично.

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

Конечно, такая ситуация может возникнуть только в отдельных программах. Но само наличие такой возможности является недостатком процедуры New, и это следует принимать во внимание при программировании. Однако в Turbo Pascal имеется эффективный способ управления кучей, в котором этот недостаток отсутствует. 

Процедуры GetMem и FreeMem

Процедура GetMem(Var P: Pointer; Razmer:Word) выделяет в куче непрерывный блок ячеек с требуемым размером в Razmer байт и адрес первой ячейки этого блока присваивает указателю Р.

Процедура FreeMem освобождает в куче непрерывный блок ячеек с размером в Razmer байт, начиная с адреса, записанного в указателе Р (Get и Free — соответственно выделять и освобождать, Mem — от Memory — память).

Процедура GetMem аналогична процедуре New, а процедура FreeMem — процедуре Dispose. Если в процедурах New и Dispose размер блока под объект динамической переменной определяется автоматически на основе типа выбранной динамической переменной (или, как часто говорят, — типа данных, на который ссылается выбранный указатель), то в процедурах GetMem и FreeMem размер такого блока должен контролироваться программистом.

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

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

Сходство упомянутых пар процедур позволяет использовать их одновременно в одной и той же программе. Более того, в паре процедур New-Dispose вместо процедуры New можно применять процедуру GetMem, вместо DisposeFreeMem и, наоборот, — в паре GetMem-FreeMem вместо GetMemNew, а вместо FreeMemDispose. К сожалению, при работе пары процедур GetMem и FreeMem также возможна фрагментация кучи.

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

Исключить фрагментацию кучи позволяет применение процедур Mark (метить) и Release (освобождать).

Процедура Mark(Var P: Pointer) запоминает в указателе Р нижнюю текущую границу свободной памяти кучи — адрес первой свободной ячейки, расположенной сразу же за конечной ячейкой объекта самой последней динамической переменной. Таким образом, в Р запоминается адрес первой ячейки неиспользуемой части кучи. В этом случае динамическая переменная-указатель фактически дублирует функции типизированной константы-указателя HeapPtr и с успехом может быть заменена ею.

Процедура Release(Var P:Pointer) возвращает кучу в состояние, которое было зафиксировано ранее в Р вызовом процедуры Mark. Если после вызова процедуры Mark(P) вы порождали (при помощи New или GetMem) целый ряд новых динамических переменных (любых типов), то вызов процедуры Release (P) после таких порождений передвигает нижнюю границу свободного пространства кучи HeapPtr с позиции (с адреса), которую она занимала после порождения последней динамической переменной, в позицию (в адрес), которая ранее была зафиксирована в указателе Р. При этом все переменные, порожденные после вызова процедуры Mark(P), уничтожаются и все смежные блоки ячеек уничтоженных переменных пригодны для размещения в них любых других динамических переменных без эффекта фрагментации.

Если перед порождением первой динамической переменной в типизированную константу-указатель HeapOrg записать исходное состояние кучи (при помощи Mark(HeapOrg)), то вызовом процедуры Release (HeapOrg) можно полностью освободить кучу от всех динамических переменных, которые до такого вызова порождались, например, вызовом процедуры New.

Приведем поясняющий фрагмент программы:

Var

 Р:Pointer;

 Pl,P2,P3,P4:^Real;

BEGIN

 New(Pl);

 New(P2);

 Mark(P);

 New(P3);

 New(P4);

 Mark(HeapPtr) ;

 Release(P)

 Mark(HeaPtr);

После вызова двух последних процедур приведенного фрагмента программы в указателях Р и PheaPtr будет записан один и тот же адрес — указатель первой ячейки кучи, расположенной за последней ячейкой объекта самой «верхней» динамической переменной Р2. Переменные РЗ и Р4, порожденные после Р2, будут уничтожены и адрес, записанный в Р, окажется адресом первой ячейки неиспользованной памяти кучи: новой нижней границы свободной области кучи.

При таком способе управления кучей нельзя удалить, например, переменную Р2, не удалив при этом переменные РЗ и Р4. Это ограничение является определенным недостатком процедур Mark и Release. Для более гибкого использования кучи необходимо применять процедуру Dispose или FreeMem.

В одной и той же программе не рекомендуется применение «блочных» процедур Mark и Release совместно с процедурами Dispose и FreeMem. Это обусловлено следующим обстоятельством. При использовании процедур Dispose и FreeMem параллельно с освобождением ими блоков памяти объектов конкретных динамических переменных адреса (координаты) освобожденных блоков заносятся в специальный список адресов и объемов свободных блоков, который применяется всякий раз для последующего размещения в куче новых динамических переменных. Процедура же Release(P) не только перемещает указатель на новую нижнюю границу свободной части кучи, но и стирает весь список свободных блоков. Таким образом, если освобожденный процедурой Dispose или FreeMem блок памяти оказался ниже указателя Р, то после применения процедуры Release (P) до конца работы программы в этом блоке больше не размещаются новые динамические переменные с использованием процедур New или GetMem. Это необходимо всегда помнить, дабы постепенно не заблокировать всю кучу от размещения там каких-либо переменных.

В заключение отметим, что хорошим стилем программирования является уничтожение динамических переменных после их обработки (если это даже не требуется для размещения новых динамических переменных) или в конце программы. Это означает, что для каждой процедуры New должна иметься парная ей процедура Dispose, а для каждой процедуры GetMem — процедура FrееМеm. Если применяется блочная работа с динамической памятью при помощи подпрограмм Маrk Re lease, то перед порождением первой динамической переменной необходимо вызвать процедуру Mark(P), а в конце программы — процедуру Release(P) для полного освобождения кучи от динамических переменных вашей программы. Нарушение этих рекомендаций не приведет к сбоям в работе программы, а будет лишь свидетельствовать о плохом стиле программирования автора программы.


 

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

43268. Методы и методики перевода 226 KB
  Современные технологии политика наука и конечно литература – без перевода все это было бы недоступно для многих наций. Когда в мире появилось как минимум два языка наверное тогда человек впервые столкнулся с проблемой адекватности перевода. Когда знания людей о каком-либо виде деятельности стали обобщаться превращаясь в науку перевод не стал исключением.
43269. Сестринский процесс при ревматоидном артрите 982.5 KB
  Ревматоидный артрит – аутоиммунное заболевание неизвестной этиологии, для которого характерны симметричный эрозивный артрит (синовиит) и широкий спектр внесуставных (системных) проявлений. Ревматоидный артрит является одним из весьма распространенных заболеваний, причем заболеваемость им за последние 50—60 лет увеличилась. Через 5 лет от начала заболевания, несмотря на лечение „базисными“ препаратами, 16% пациентов теряют трудоспособность, а через 20 лет – 90%, треть всех пациентов становятся полными инвалидами.
43270. Історія, види та характеристики веб-браузерів 579 KB
  Але доступ до нього неможливий без багатьох факторів одним з яких є Webбраузери. Webбраузери це програмне забезпечення для комп’ютера під’єднаного до Інтернет що дає можливість працювати з текстом малюнками або іншою інформацією на вебсторінці. Поняття веббраузера Браузер це програма що представляє в зручному для сприйняття вигляді інформацію що отримується з Інтернету.
43271. Решение задач оптимизации 730.5 KB
  Цель задания: Найти минимум функции методами прямого поиска и градиентными методами. Цель работы отработка навыков решения задач безусловной оптимизации функции нескольких переменных методами прямого поиска и отработка навыков решения задач безусловной оптимизации градиентными методами. Решена задача безусловной оптимизации функции нескольких переменных методами прямого поиска и градиентными методами. Нахождение стационарной точки Целевая функция: ; Частные производные f по x1 и x2: ∂f ∂x1=2x1 x2 –6; ∂f ∂x2=x1 2x2 –4; Приравниваем...
43272. Синтез линейной дискретной антенны с равномерным амплитудным распределением 892.5 KB
  Акустическая антенна обычно состоит из электроакустических преобразователей элементов антенны акустических экранов несущей конструкции акустических развязок амортизаторов и линий электрокоммуникаций. По способу создания пространственной избирательности антенны можно подразделять на интерференционные фокусирующие рупорные и параметрические. Интерференционные антенны можно подразделять на непрерывные и дискретны. И непрерывные и дискретные антенны подразделяют по конфигурации геометрического образования объединяющего активные...
43273. Программное обеспечение для учета имеющегося товара в строительном магазине 812 KB
  Для разработки программы использовалось динамическое программирование которое подразумевает под собой разделение одной сложной задачи на несколько более простых. Раздел описания модулей USES. Раздел имеет структуру: USES Модуль 1 Модуль 2. Если таких обращений нет то раздел USES не нужен.
43274. УСИЛИТЕЛЬ МОЩНОСТИ СИГНАЛОВ ЗВУКОВОЙ ЧАСТОТЫ 294 KB
  По входным характеристикам выбираем рабочую точку транзистора оконечного каскада для режима АВ. Мощность потребляемая базовой цепью от предыдущего транзистора Расчет коллекторной цепи второго транзистора. Изза нелинейности характеристик транзистора целесообразно выбирать: Максимальный ток коллектора: Амплитуда переменного напряжения на коллекторе: а максимальное напряжение между коллектором и эмиттером: Мощность потребляемая коллекторной цепью транзистора от источника питания: Мощность рассеиваемая на коллекторе: Выбор второго...
43275. Расчет параметра конденсатора 108.5 KB
  В настоящее время продолжают находить широкое применение аналоговые датчики которые позволяют преобразовывать механические параметры например длину пути в электрический сигнал. Среди всего многообразия измеряемых параметров значительное место занимают датчики измерения угловых и линейных перемещений пути. Возможностью измерения в широком диапазоне от долей мм до 2м и с достаточной точностью измерений обладают емкостные датчики [1]. Датчики Датчиком называется первичный элемент автоматической системы реагирующий на изменение физической...
43276. Помехоустойчивое кодирование 367 KB
  Безусловно не все ошибки могут быть обнаружены. Существует вероятность того что несмотря на возникшие ошибки принятая последовательность кодовых символов окажется разрешенной комбинацией но не той которая передавалась. Однако при разумном выборе кода вероятность необнаруженной ошибки т. Если действительно передавалась bj то тем самым возникшие ошибки будут исправлены.