Защитное программирование

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

ð ошибки в программе

ð ошибки в спецификациях

ð ошибки в данных

ð ошибки в передаче данных

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

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

АБСТРАКТНЫЕ ТИПЫ ДАННЫХ

Типом данных называется совокупность возможных значений и операций над этими значениями.

Абстрактным типом данных называется тип данных, реализация которого скрыта от пользователя. То есть пользователь знает возможные значения этого типа, и операции над этими значениями, но не знает, как именно реализован этот тип.

Классифицируются типы данных по скорости доступа к элементу структуры и размером памяти занимаемой этой структурой. По скорости доступа различают следующие типы данных:

ð структуры прямого доступа (динамический вектор).

ð структуры последовательного доступа (стек).

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

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

ðстатические структуры

ðдинамические структуры

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

СТРУКТУРА ДАННЫХ - СТАТИЧЕСКАЯ ТАБЛИЦА.

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

Ø Создание таблицы заданных размеров.

Ø Уничтожение таблицы.

Ø Доступ к размерам таблицы (операция наблюдатель).

Ø Доступ к чтению элемента по индексу (операция наблюдатель).

Ø Доступ к элементу для изменения (модификатор).

 
  защитное программирование - student2.ru

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

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

1. В памяти выделяется фиксированная область в виде двумерного массива, довольно больших размеров, заведомо превышающих размеры таблицы:

 
  защитное программирование - student2.ru

2. В памяти выделяется область статической памяти (одномерный вектор),

 
  защитное программирование - student2.ru

защитное программирование - student2.ru в которую последовательно записываются строки таблицы:

защитное программирование - student2.ru
Данный способ лучше, но доступ более долгий и трудоемкий.

3. Этот способ является аналогом способа 2, но использует динамически выделенную память, то есть сначала задаются размеры таблицы, затем рассчитывается длина одномерного куска памяти и выделяется необходимое количество памяти в куче.

4. Недостаток способа №3 – это ограничение по памяти, то есть когда программа размещается в ОЗУ, ей соответствует некоторая область памяти, в которую записываются сначала все типизированные константы, а затем все глобальные переменные, данная область называется сегментом данных и имеет размер в 64Кб. То есть существует ограничение на размер структуры.

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

 
  защитное программирование - student2.ru

Замечание: Этот способ имеет более быстрый доступ к элементам таблицы и дает возможность создавать таблицы больших размеров.

Следующий модуль демонстрирует использование метода №3.

Unit ATable;

{$N+} {подключение числового сопроцессора}

Interface{интерфейсная часть}

Type _Month = (jan,feb,mar,apr,may,jul,jun,avg,sep,oct,nov,dec);

_Ind1 = _Month; {тип столбца}

_Ind2 = integer; {тип строки}

_Elem = single; {тип элементов таблицы}

Ptable = pointer; {нетипизированный указатель не таблицу}

Procedure Create (var pt : Ptable; low1,high1 : _Ind1; low2,high2 : _Ind2 ; x:_Elem);

{процедура инициализации таблицы, low1,high1,low2,hihg2- начальные и конечные значения индексов, pt – указатель на таблицу, х-элемент, которым изначально заполняется таблица}

Procedure Destroy (var pt : Ptable);

Function GetLow1 ( pt : Ptable) :_Ind1;

Function GetLow2 ( pt : Ptable) :_Ind2;

Function GetHigh1 ( pt : Ptable) :_Ind1;

Function GetHigh2 ( pt : Ptable) :_Ind2;

{Функции GetLow1,GetLow2,GetHigh1,GetHihg2 принимают в качестве параметра указатель на таблицу и возвращают начальные и конечные знечения индекса}

Function Get ( pt:Ptable; i1 : _Ind1; i2 : _Ind2):_Elem; {возвращает элемент (i1,i2)}

Procedure Put ( pt:Ptable; i1 : _Ind1; i2 : _Ind2; x: _Elem); {записывает элемент(i1,i2)}

Implementation{раздел описаний}

Uses Crt;

Type

AT = Array[0..0] of _Elem; {одномерный массив }

Pat = ^AT; {указатель на одномерный массив}

Rep = Record

l1,h1 : _Ind1;

l2,h2 : _Ind2;

P : PAT;

size1,size2 : word;

End;

Prep = ^Rep; {указатель на запись, в которой находятся все параметры таблицы}

Procedure failure( n: byte); {внутренняя процедура обработки ошибок}

Begin

ClrScr;

Writeln('Ошибка : ');

Case n of

1 : Writeln('недостаточно памяти для создания таблицы');

2 : Writeln('неверные границы при создании таблицы');

3 : Writeln('неверные индексы при доступе для чтения');

4 : Writeln(' неверные индексы при доступе для записи' );

End;

Halt(1); {выход в операционную среду}

End;

Procedure Create;

Var v : Prep; k : longint;

Begin

if sizeof(Rep)>MaxAvail then failure(1); {проверка свободной памяти в куче}

New(v); {освобождение места под запись Rep}

if (Low1>High1) or (low2>high2) then failure(2);

{проверка вводимых индексов}

with v^ do

begin

l1:=low1; {параметры таблицы}

l2:=low2; {параметры таблицы}

h1:=high1; {параметры таблицы}

h2:=high2; {параметры таблицы}

size1 := ord(h1)-ord(l1)+1;{расчет длины строки}

size2 := ord(h2)-ord(l2)+1; {расчет длины столбца}

k := longint(size1)*longint(size2)*sizeof(_Elem);

{расчет памяти необходимой для таблицы}

if(k>MaxAvail)or(k>65520) then failure(1);

{проверка на возможность размещения таблицы в одном сегменте кучи }

getmem(p,k); {выделение необходимой памяти для таблицы}

for k:=1 to size1+size2-1 do P^[k]:= x; {заполнение таблицы элементом х}

end;

Pt:=v;

end;

Procedure destroy; {процедура уничтожения таблицы }

var k : longint;

begin

with Prep(Pt)^ do

begin

k:= longint(size1)*longint(size2)*sizeof(_Elem);

freemem(p,k); {освобождение памяти от таблицы}

size1:=0;size2:=0;p:= nil;

l1:=_Ind1(1); h1:=_ind1(0);

l2:=_Ind2(1); h2:=_ind2(0);

end;

dispose(Prep(pt)) {освобождение памяти от всей структуры};

Pt:=nil;

end;

function Get; {процедура просмотра элемента (i1,i2)}

var k : word;

begin

with Prep(Pt)^ do

begin

if (i1<l1) or (i1>h1) or (i2<l2) or (i2>h2) then failure(3); {проверка индексов}

k:= (ord(i1)-ord(l1))*size2 + (ord(i2)-ord(l2))*size1; {расчет местоположения элемента}

Get := P^[k]; {возвращение элемента}

end;

end;

procedure Put; {процедура записи элемента}

var k : word;

begin

with Prep(Pt)^ do

begin

if (i1<l1) or (i1>h1) or (i2<l2) or (i2<h2) then failure(4); {проверка индексов}

k:= (ord(i1)-ord(l1))*size2 + (ord(i2)-ord(l2))*size1; {расчет местоположения элемента в массиве}

P^[k] := x; {запись в массив}

end;

end;

Function GetLow1; {возвращает начальный индекс строки }

begin

GetLow1 :=PRep(Pt)^.l1;

end;

Function GetLow2;

begin

GetLow2 :=PRep(Pt)^.l2;

end;

Function GetHigh2;

begin

GetHigh2 :=PRep(Pt)^.h2;

end;

Function GetHigh1;

begin

GetHigh1 :=PRep(Pt)^.h1;

end;

end.

begin

end.

 
  защитное программирование - student2.ru

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

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

Наши рекомендации