Ошибки при работе с динамической памятью
Поможем в ✍️ написании учебной работы
Поможем с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой

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

1. Попытка воспользоваться неинициализированным указателем.

var pi: ^integer;

i: integer;

Begin

pi^:=5;

Если pi – глобальная переменная, то она автоматически инициализируется нулевым значением, т.е. имеет значение nil. Разыменование нулевого указателя приводит к ошибке времени выполнения. Если pi – локальная переменная, то она по умолчанию не инициализируется, а поэтому содержит непредсказуемое значение. Это значение трактуется как адрес целой переменной, к которой осуществляется доступ. Как правило, в этой ситуации возникает исключение Access Violation (нарушение защиты доступа), но по чистой случайности может оказаться, что указатель pi содержит истинный адрес переменной программы, тогда переменная будет изменена, выполнение программы продолжится дальше, а факт изменения переменной непредсказуемым образом повлияет на дальнейшее выполнение программы.

 

2. «Висячие» указатели.

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

var pi: ^integer;

Begin

New(pi);

pi^:=5;

Dispose(pi); // указатель становится "висячим"

...

pi^:=6; // ошибка!

Если после Dispose(pi) сразу написать pi:=nil, то в дальнейшем при попытке разыменовать нулевой указатель pi возникнет исключение, что является более предпочтительным, чем скрытая ошибка изменения другой переменной. Данный прием следует взять на вооружение и после освобождения динамической переменной обнулять указатель:

Dispose(pi);

pi:=nil;

3. «Утечка» памяти.

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

Пример 1. Повторное выделение памяти.

Если выделить память повторно для того же указателя, то ранее выделенная память «утечет»:

var pi: ^integer;

Begin

New(pi);

pi^:=5;

New(pi);

 

Пример 2. Выделение памяти под локальную переменную без освобождения.

procedure pp;

var pi: ^integer;

Begin

New(pi);

end;

Данная процедура составлена ошибочно: локальный указатель pi уничтожается после завершения работы процедуры, поэтому контролируемая им динамическая память «утекает». Особенно опасна подобная утечка при вызове такой процедуры в цикле:

for i:=1 to MaxInt do

pp;

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

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

5. Попытка освободить динамическую память, не выделенную ранее.

var pi: ^integer;

Begin

Dispose(pi);

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

6. Попытка дважды освободить занимаемую память.

var pi: ^integer;

Begin

New(pi);

...

Dispose(pi);

Dispose(pi);

При повторном вызове процедуры Dispose будет сгенерировано исключение.

7. Попытка освободить нединамическую память.

var pi: ^integer;

i: integer;

Begin

pi:=@i;

Dispose(pi);

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

8. Выход за память, выделенную процедурой GetMem.

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

GetMem(dyn,n*sizeof(integer));

dyn^[n+1]:=5;

Обычно такая ошибка приводит к исключению Access Violation.

Задание 1

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

1. посчитать сумму цифр данного целого числа;

2. посчитать сумму делителей данного целого числа;

3. определить, является ли данное целое число простым;

4. посчитать N-е число Фибоначчи;

5. посчитать сумму первых N чисел Фибоначчи;

6. посчитать количество трехзначных чисел с суммой цифр 13.

Задание 2

Составьте программу, которая формирует максимально длинный одномерный массив целых чисел в динамической памяти и заполняет его случайными целыми числами из диапазона от 0 до 20.

Задание 3

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

Задание 4

Составьте процедуру для упорядочения сформированного массива.

 

Лабораторная работа №11

Работа со списк ами

Теоретические сведения

Дата: 2019-02-02, просмотров: 339.