Лекция № 6. Структурное и «неструктурное» программирование

Содержание лекции: понятия структурного и «неструктурного» программирования; программирование с «защитой от ошибок»; сквозной структурный контроль.

 

Цель лекции: выявить разницу между структурным и «неструктурным» программированием, а также приемы обеспечения защиты от ошибок.

 

Одним из способов обеспечения высокого уровня технологичности разрабатываемого ПО является структурное программирование. Различают три вида вычислительного процесса, реализуемого программами: линейный, разветвленный и циклический. Линейная структура процесса вычислений предполагает, что для получения результата необходимо выполнить операции в определен­ной последовательности. При разветвленной структуре процесса вычислений конкретная последовательность операций зависит от значений одной или нескольких переменных. Для по­лучения результата при циклической структуре некоторые действия необходимо выполнить несколько раз. Для реализации этих вычислительных процессов в программах используют соответствующие управляющие операторы. Для изображения схем алгоритмов таких программ был разработан ГОСТ 19.701-90, согласно которому каждой группе дейст­вий ставится в соответствие специальный блок. Стан­дарт предусматривает блоки для обозначения циклов, не запрещает произвольной передачи управления и допускает использование команд услов­ной и безусловной передачи управления при реализации алгоритма.

После того, как в 60-х годах XX в. было доказано, что любой сложный алгоритм можно представить, используя три основные управляющие конструкции, в языках программирования высокого уровня появились управляющие операторы для их реализации [6]. К базовым относят:

а) следование - обозначает последовательное выполнение действий;

б) ветвление - выбор одного из двух вариантов действий;

в) цикл-пока - определяет повторение действий, пока не будет нарушено некоторое условие, выполнение которого проверяется в начале цикла.

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

а) выбор - выбор одного варианта из нескольких в зависимости от значения некоторой величины;

б) цикл-до - повторение действий до выполнения заданного условия, проверка которого осуществляется после выполнения действий в цикле;

в) цикл с заданным числом повторений (счетный цикл) - повторение некоторых действий указанное количество раз.

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

а) низкий уровень детализации, что скрыва­ет суть сложных алгоритмов;

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

Кроме схем, для описания алгоритмов можно использовать псевдокоды, Flow-формы и диаграммы Насси-Шнейдермана, которые базируются на тех же основных структурах, допускают разные уровни детализации и делают невозможным описание неструктурных алгоритмов [1].

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

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

Диаграммы Насси-Шнейдерманаявляются развитием Flow-форм лишь с той разницей, что область обозначения условий и вариантов ветвления изоб­ражают в виде треугольников (таблица В.1), обеспечивающих большую наглядность представления алгоритма.

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

С точки зрения технологичности хорошим считают стиль оформления программы, облегчающий ее восприятие, как самим автором, так и другими программистами, которым придется ее проверять или модифицировать. Автор известной монографии, посвященной проблемам программирования, Д. Ван Тассел призывал: «Помните, программы читаются людьми» [7]. Исходя из того, что любую программу придется неоднократно просматривать, следует придерживаться хорошего стиля написания программ, который включает:

а) правила именования объектов программы;

б) правила оформления модулей;

в) стиль оформления текстов модулей.

Любая из ошибок программирования, которая не обнаруживается на этапах компиляции и компоновки программы, в итоге может проявиться тремя способами: привести к выдаче системного сообщения об ошибке, «зависанию» компьютера и получению неверных результатов. Однако до того, как результат работы программы становится фатальным, ошибки много раз проявляются в виде неверных типах дан­ных, неверных промежуточ­ных результатах, неверных управляющих переменных, индексах структур данных и т. п. (рисунок В.1). Часть ошибок можно обнаружить и нейтрализовать, пока они не привели к тяжелым последствиям. Программирование, при котором применяют специальные приемы раннего обнаружения и нейтрализации ошибок, названо защитнымили программированием «с защитой от ошибок».Детальный анализ ошибок и их возможных ранних проявлений показывает, что целесообразно проверять правильность выполнения операций ввода-вывода, а также допустимость промежуточных результатов (значений управляющих переменных, индексов, типов данных, числовых аргументов).

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

а) ошибки передачи - аппаратные средства искажают данные;

б) ошибки преобразования - программа неверно преобразует исходные данные из входного формата во внутренний;

в) ошибки перезаписи - пользователь ошибается при вводе данных;

г) ошибки данных — пользователь вводит неверные данные.

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

Для защиты от ошибок преобразования данные после ввода сра­зу демонстрируют пользователю («эхо»), выполняя пре­образование сначала во внутренний формат и обратно. Предотвратить все ошибки преобразования на этом этапе крайне сложно, поэто­му фрагменты программы тестируют [15], ис­пользуя методы эквивалентного разбиения и граничных значений.

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

Неверные данные обычно может обнаружить только пользователь.

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

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

Для снижения погрешности результатов вычислений следует

а) избегать вычитания близких чисел (машинный ноль);

б) избегать деления больших чисел на малые;

в) сложение длинной последовательности чисел начинать с меньших по абсолютной величине;

г) стремиться по возможности уменьшать количество операций;

д) использовать методы с известными оценками погрешностей;

е) не использовать условие равенства вещественных чисел;

ж) вычислять с двойной точностью, а результат выдавать - с одинарной.

Поскольку полный контроль данных на входе и в процессе вычислений невозможен, следует предусматри­вать перехват и обработку аварийных ситуаций. Для перехвата и обработки аппаратно и программно фиксируемых ошибок в некоторых языках программирования (Delphi Pascal, C++ и Java) предусмотрены средства обработки исключений. Использование этих средств позволяет не допустить выдачи пользователю сообщения об аварий­ном завершении программы, а программист получает возможность предусмотреть действия, позволяющие исправить эту ошибку или выдать пользователю сооб­щение с точным описанием ситуации и продолжить работу.

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

Дополнительную информацию по теме можно получить в [1, 6, 7, 15].