Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Информатика 2 семестр.doc
Скачиваний:
14
Добавлен:
01.04.2025
Размер:
609.28 Кб
Скачать

2.4. Обработка ошибок, возникающих в действиях с файлами

Останов программы, возникающий из-за ошибок, связанных с файловыми операциями, может быть предотвращен и заменен соответствующим диалогом с пользователем. Для этого необходимо перед очередной файловой операцией в программе отменить директивой {$I-} действующий по умолчанию (директива {$I+}) автоматический контроль ошибок. В этом случае становится доступной для применения функция без параметров IOResult. Она возвращает код ошибки (0 – если нет ошибки), а также разблокирует операции ввода/вывода. Покажем на иллюстративном примере вариант организации диалога с пользователем в случае возникновения ошибок этапа выполнения (Run-Time).

program Obrabotka_Oshibok_Rabot_s_Files;

var

f : text;

s : string;

kod : byte;

begin

{$I-} {Отключение автоконтроля}

repeat

writeln('Введите имя файла'); readln(s);

assign(f, s);

reset(f); {Файловая операция}

kod := IOResult; {Код результата}

if kod <> 0 then begin {Возникла ошибка}

writeln('Ошибка при открытии файла');

case kod of

2 : writeln('Файл не найден'); {Коды ошибок}

3 : writeln('Путь не найден') {времени выполнения}

end {case}

end {if}

until kod = 0;

{$I+} {Пример действий – вывод строк из файла на экран}

repeat

readln(f, s);

writeln(s)

until eof(f)

end.

3. Указатели - ссылочные типы данных

До сих пор нами рассматривались статические структуры данных. Память под них выделяется при компиляции. Имеется другой способ выделения и освобождения памяти – динамический, при котором память распределяется в процессе выполнения программы. Производится это с помощью особых типов данных – указателей. Именно указатели позволяют реализовать одну из основных технологий современных операционных систем, базирующуюся на библиотеках динамической компоновки (DLL – Dynamic Linking Library). В памяти компьютера имеются области для системных целей, сегмента стека, выполняемых программ. Вся не занятая названными областями память может быть использована в процессе выполнения программ и потому получила название динамически распределяемой памяти (ДРП) или кучи (Heap). По умолчанию минимальный и максимальный размеры кучи составляют соответственно 0 и 655360 байтов.

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

var <Имя> : pointer; {Нетипизированный указатель}

<Имя> : ^ <Базовый тип>; {Указатель на базовый тип}

Символ '^' читается как 'указатель на'. В качестве примера опишем указатель на элемент некоторого списка, размещаемого в ДРП. Элемент имеет два поля: информационное и адресное. При том возникает ситуация, в которой нарушается требование описания объекта до его использования. Для указателей сделано единственное исключение из названного правила.

type

point = ^element; {Указатель на тип element}

element = record

data : word; {Поле данных}

next : point {Поле указателя}

end;

var

p, q : point; {Переменные типа указатель}

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

p := @<Переменная> или q := Addr(<Переменная>).

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

Seg(<Переменная>) и Ofs(<Переменная>).

При этом следует учитывать то обстоятельство, что динамические переменные не имеют собственного имени и обращаются к ним через указатель с помощью конструкции, получившей название разыменования. Так, запись p^ означает 'переменная, на которую ссылается указатель p' (это и будет динамическая переменная). Зная сегмент и смещение, можно найти значение указателя с помощью стандартной функции ptr:

p := ptr(<Сегмент>, <Смещение>).

Указатель занимает 4 байта памяти: 2 – номер сегмента и 2 – смещение.

Динамическое выделение и освобождение памяти производят для типизированного указателя p процедурами New(p) и Dispose(p) соответственно, а для указателя q любого типа – GetMem(q, Size) и FreeMem(q, Size). Второй параметр последней пары процедур задает размер памяти в байтах. Процедуры должны использоваться только в таком сочетании друг с другом. Размер поля, выделяемого процедурами New и GetMem, всегда доводится до кратного 8. Пока динамические переменные не созданы, значение указателя не определено. В этом случае переменной типа указатель можно присваивать значение nil. Константа nil означает отсутствие ссылки на какой-либо объект и распознается системой.

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

Пример 1. Создадим с помощью трех указателей три элемента в ДРП

a b c

и свяжем их между собой так, чтобы поле ссылки первого элемента содержало адрес второго, поле ссылки которого – адрес третьего элемента. После установления таких связей можно ‘обнулить’ исходные указатели b и c.

a b := nil c := nil

nil

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

program ukazateli;

type

pnt = ^z; {Тип указатель на базовый тип z}

z = record

inf : word; {Информационное поле}

uk : pnt {Поле ссылки}

end;

var

a, b, c : pnt;

begin

writeln('Свободно ', MemAvail, ' байтов');

{Выделение памяти под три динамические переменные}

new(a); new(b); new(c);

writeln('Свободно ', MemAvail, ' байтов');

a^.inf := 2; {Заполнение полей данных записей}

b^.inf := 4;

c^.inf := 11;

a^.uk: = b; {Связываем элементы друг с другом}

b^.uk := c;

c^.uk := nil; {Последний элемент ни на что не ссылается}

b := nil; c := nil; {‘Обнуляем’ ссылки на второй и третий элементы списка}

writeln('Значение информационного поля третьего элемента:');

writeln(a^.uk^.uk^.inf); {Оно доступно через соответствующие указатели}

repeat {Освобождение динамической памяти}

b := a;

a := a^.uk;

dispose(b)

until a = nil;

writeln('Свободно ', MemAvail, ' байтов');

writeln('Нажмите ENTER'); readln

end.

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

program Gros_Mas;

type

Stroka = array[1..100] of real; {Базовый тип}

var

p : array[1..200] of ^Stroka; {Массив указателей на строки}

j, i : byte;

begin

writeln('Свободно до размещения массива в ДРП', MemAvail, ' байтов');

for i:=1 to 200 do begin

GetMem(p[i], SizeOf(Stroka)); {Выделение памяти под i-ю строку матрицы}

for j := 1 to 100 do

p[i]^[j] := i + j; {Заполнение i-й строки матрицы}

end;

writeln('Свободно после размещения массива в ДРП ', MemAvail, ' байтов');

writeln('Контрольный вывод второго и 99-го элементов из 1 и 100 строк');

writeln(p[1]^[2] : 2 : 0);

writeln(p[100]^[99] : 4 : 0);

for i:=1 to 200 do {Очистка памяти}

FreeMem(p[i], SizeOf(Stroka));

writeln('Нажмите ENTER'); readln end.

Среди стандартных средств языка имеются следующие типизированные константы типа pointer. Для обозначения начала ДРП служит инициализированная переменная HeapOrg, а ее конца – HeapEnd. На край занятой части ДРП указывает HeapPtr. Процедура Release(<Нетипизированный указатель>) освобождает участок ДРП, начало которого обозначает <Нетипизированный указатель>, а конец – HeapPtr. Оператор Release(HeapOrg); освобождает всю ДРП. Процедуру не следует использовать попеременно с процедурами Dispose и FreeMem.