ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 20.11.2019
Просмотров: 9494
Скачиваний: 184

2.5.
Стиль
оформления
программы
С
точки
зрения
технологичности
хорошим
считают
стиль
оформления
программы
,
облегчающий
ее
восприятие
как
самим
автором
,
так
и
другими
программистами
,
которым
,
возможно
,
придется
ее
проверять
или
модифицировать
. «
Помните
,
программы
читаются
людьми
»,
призывал
Д
.
Ван
Тассел
,
автор
одной
из
известных
монографий
,
посвященной
проблемам
программирования
[60].
Именно
исходя
из
того
,
что
любую
программу
неоднократно
придется
просматривать
,
следует
придерживаться
хорошего
стиля
написания
программ
.
Стиль
оформления
программы
включает
:
•
правила
именования
объектов
программы
(
переменных
,
функций
,
типов
,
данных
и
т
.
п
.);
•
правила
оформления
модулей
;
•
стиль
оформления
текстов
модулей
.
Правила
именования
объектов
программы
.
При
выборе
имен
программных
объектов
следует
придерживаться
следующих
правил
:
•
имя
объекта
должно
соответствовать
его
содержанию
,
например
:
MaxItem -
максимальный
элемент
;
NextItem -
следующий
элемент
;
•
если
позволяет
язык
программирования
,
можно
использовать
символ
«_»
для
визуального
разделения
имен
,
состоящих
из
нескольких
слов
,
например
:
Max_Item, Next_Itetm;
необходимо
избегать
близких
по
написанию
имен
,
например
:
Index
и
InDec.
Правила
оформления
модулей
.
Каждый
модуль
должен
предваряться
заголовком
,
который
,
как
минимум
,
содержит
:
•
название
модуля
;
•
краткое
описание
его
назначения
;
•
краткое
описание
входных
и
выходных
параметров
с
указанием
единиц
измерения
;
•
список
используемых
(
вызываемых
)
модулей
;
•
краткое
описание
алгоритма
(
метода
)
и
/
или
ограничений
;
•
ФИО
автора
программы
;
•
идентифицирующую
информацию
(
номер
версии
и
/
или
дату
последней
корректировки
).
Например
:

Стиль
оформления
текстов
модулей
.
Стиль
оформления
текстов
модулей
определяет
использование
отступов
,
пропусков
строк
и
комментариев
,
облегчающих
понимание
программы
.
Как
правило
,
пропуски
строк
и
комментарии
используют
для
визуального
разделения
частей
модуля
,
например
:
{
проверка
количества
отрезков
и
выход
,
если
отрезки
не
заданы
}
ifn<0 then
begin
WrileLn ('
Количество
отрезков
отрицательно
');
exit;
end;
{
цикл
суммирования
длин
отрезков
}
S: = 0;
for i: = 0 to n-l do S: = S + Len [i];
Для
таких
языков
,
как
Pascal, C++
и
Java,
использование
отступов
позволяет
прояснить
структуру
программы
:
обычно
дополнительный
отступ
обозначает
вложение
операторов
языка
,
например
:
а
m
ах
: =
а
[1,1];
for i: = l to n do
for j: = l to
т
do
ifa[i,j]>amax then amax: = a [i,j];
Несколько
сложнее
дело
обстоит
с
комментариями
.
Опыт
показывает
,
что
переводить
с
английского
языка
каждый
оператор
программы
не
нужно
:
любой
программист
,
знающий
язык
программирования
,
на
котором
написана
программа
,
без
труда
прочитает
тот
или
иной
оператор
.
Комментировать
следует
цели
выполнения
тех
или
иных
действий
,
а
также
группы
операторов
,
связанные
общим
действием
,
т
.
е
.
комментарии
должны
содержать
некоторую
дополнительную
(
неочевидную
)
информацию
,
например
:
{
проверка
условия
и
выход
,
если
условие
не
выполняется
}
ifn<0 then
begin
WriteLn('Kojiit4ecmeo
отрезков
отрицательно
');
exit;
end;
Для
языков
низкого
уровня
,
например
,
Ассемблера
,
стиль
,
облегчающий
понимание
,
предложить
труднее
.
В
этом
случае
может
оказаться
целесообразным
комментировать
и
блоки
операторов
,
и
каждый
оператор
,
например
:
;
цикл
суммирования
элементов
массива
;
установки
цикла
mov AX, 0
;
обнуляем
сумму
mov CX,
п
;
загружаем
счетчик
цикла
mov BX, 0
;
смещение
первого
элемента
массива
;
тело
цикла
cycle:
add AX, a [BX]
;
добавляем
элемент
add BX, 2
;
определяем
адрес
следующего
loop cycle
;
цикл
на
n
повторений
;
выход
из
цикла
при
обнулении
счетчика

2.6.
Эффективность
и
технологичность
Традиционно
эффективными
считают
программы
,
требующие
минимального
времени
выполнения
и
/
или
минимального
объема
оперативной
памяти
.
Особые
требования
к
эффективности
программного
обеспечения
предъявляют
при
наличии
ограничений
(
на
время
реакции
системы
,
на
объем
оперативной
памяти
и
т
.
п
.).
В
случаях
,
когда
обеспечение
эффективности
не
требует
серьезных
временных
и
трудовых
затрат
,
а
также
не
приводит
к
существенному
ухудшению
технологических
свойств
,
необходимо
это
требование
иметь
в
виду
.
Разумный
подход
к
обеспечению
эффективности
разрабатываемого
программного
обеспечения
состоит
в
том
,
чтобы
в
первую
очередь
оптимизировать
те
фрагменты
программы
,
которые
существенно
влияют
на
характеристики
эффективности
.
Для
уменьшения
времени
выполнения
некоторой
программы
в
первую
очередь
следует
проанализировать
циклические
фрагменты
с
большим
количеством
повторений
:
экономия
времени
выполнения
одной
итерации
цикла
будет
умножена
на
количество
итераций
.
Не
следует
забывать
и
о
том
,
что
многие
способы
снижения
временных
затрат
приводят
к
увеличению
емкостных
и
,
наоборот
,
уменьшение
объема
памяти
может
потребовать
дополнительного
времени
на
обработку
.
И
тем
более
не
следует
«
платить
»
за
увеличение
эффективности
снижением
технологичности
разрабатываемого
программного
обеспечения
.
Исключения
возможны
лишь
при
очень
жестких
требованиях
и
наличии
соответствующего
контроля
за
качеством
.
Частично
проблему
эффективности
программ
решают
за
программиста
компиляторы
.
Средства
оптимизации
,
используемые
компиляторами
,
делят
на
две
группы
:
•
машинно
-
зависимые
,
т
.
е
.
ориентированные
на
конкретный
машинный
язык
,
выполняют
оптимизацию
кодов
на
уровне
машинных
команд
,
например
,
исключение
лишних
пересылок
,
использование
более
эффективных
команд
и
т
.
п
.;
•
машинно
-
независимые
выполняют
оптимизацию
на
уровне
входного
языка
,
например
,
вынесение
вычислений
константных
(
независящих
от
индекса
цикла
)
выражений
из
циклов
и
т
.
п
.
Естественно
,
нельзя
вмешаться
в
работу
компилятора
,
но
существует
много
возможностей
оптимизации
программы
на
уровне
команд
.
Способы
экономии
памяти
.
Принятие
мер
по
экономии
памяти
предполагает
,
что
в
каких
-
то
случаях
эта
память
неэкономно
использовалась
.
Учитывая
,
что
анализировать
имеет
смысл
только
операции
размещения
данных
,
существенно
влияющие
на
характеристику
эффективности
,
следует
обращать
особое
внимание
на
выделение
памяти
под
данные
структурных
типов
(
массивов
,
записей
,
объектов
и
т
.
п
.).
Прежде
всего
при
наличии
ограничений
на
использование
памяти
следует
выбирать
алгоритмы
обработки
,
не
требующие
дублирования
исходных
данных
структурных
типов
в
процессе
обработки
.
Примером
могут
служить
алгоритмы
сортировки
массивов
,
выполняющие
операцию
в
заданном
массиве
,
например
,
хорошо
известная
сортировка
методом
«
пузырька
».
Если
в
программе
необходимы
большие
массивы
,
используемые
ограниченное
время
,
то
их
можно
размещать
в
динамической
памяти
и
удалять
при
завершении
обработки
.
Также
следует
помнить
,
что
при
передаче
структурных
данных
в
подпрограмму
«
по
значению
»
копии
этих
данных
размещаются
в
стеке
.
Избежать
копирования
иногда
удается
,
если
передавать
данные
«
по
ссылке
»,
но
как
неизменяемые
(
описанные
const).
В
последнем
случае
в
стеке
размещается
только
адрес
данных
,
например
:
Type Mas.4iv = array [I.. 100] of real;
function Summa (Const a:Massiv; ...)...
Способы
уменьшения
времени
выполнения
.
Как
уже
упоминалось
выше
,
для
уменьшения
времени
выполнения
в
первую
очередь
необходимо
анализировать
циклические
участки
программы
с
большим
количеством
повторений
.
При
их
написании
необходимо
по
возможности
:
•
выносить
вычисление
константных
,
т
.
е
.
не
зависящих
от
параметров
цикла
,
выражений
из

циклов
;
•
избегать
«
длинных
»
операций
умножения
и
деления
,
заменяя
их
сложением
,
вычитанием
и
сдвигами
;
•
минимизировать
преобразования
типов
в
выражениях
;
•
оптимизировать
запись
условных
выражений
-
исключать
лишние
проверки
;
•
исключать
многократные
обращения
к
элементам
массивов
по
индексам
(
особенно
многомерных
,
так
как
при
вычислении
адреса
элемента
используются
операции
умножения
на
значение
индексов
) -
первый
раз
прочитав
из
памяти
элемент
массива
,
следует
запомнить
его
в
скалярной
переменной
и
использовать
в
нужных
местах
;
•
избегать
использования
различных
типов
в
выражении
и
т
.
п
.
Рассмотрим
следующие
примеры
.
Пример
2.2.
Пусть
имеется
цикл
следующей
структуры
(Pascal):
for y: = 0 to 99 do
for x: = 0 to 99 do
a [320*x+y]: = S [k,l];
В
этом
цикле
операции
умножения
и
обращения
к
элементу
S[k]
выполняются
10000
раз
.
Оптимизируем
цикл
,
используя
,
что
320 = 2
8
+ 2
6
:
skl: =S [k,l];
{
выносим
обращение
к
элементу
массива
из
цикла
}
for x: = 0 to 99 do (
меняем
циклы
местами
}
begin
i:= x shl 8 + x shl 6; {
умножение
заменяем
на
сдвиги
и
выносим
из
цикла
}
for y; = 0 to 99 do
a [i+y]: =skl;
end; ...
В
результате
вместо
10000
операций
умножения
будут
выполняться
200
операций
сдвига
,
а
их
время
приблизительно
сравнимо
со
временем
выполнения
операции
сложения
.
Обращение
к
элементу
массива
S[k]
будет
выполнено
один
раз
.
Пример
2.3.
Пусть
имеется
цикл
,
в
теле
которого
реализовано
сложное
условие
:
for k: = 2 to n do
begin
ifx[k] > yk then S: = S+y[k]-x [k];
if (x [k]< = yk) and (y[k]<yk) then S: = S+yk-x[k];
end;...
В
этом
цикле
можно
убрать
лишние
проверки
:
for k: =2 to n do
begin
ifx [k]>yk then S:=S+y[k]-x[k]
else
ify[k]<yk then S: =S+yk-x [k];
end;...
Обратите
внимание
на
то
,
что
в
примере
2.2
понять
,
что
делает
программа
,
стало
сложнее
,
а
в
примере
2.3 -
практически
нет
.
Следовательно
,
оптимизация
,
выполненная
в
первом
случае
,
может
ухудшить
технологичность
программы
,
а
потому
не
очень
желательна
.

2.7.
Программирование
«
с
защитой
от
ошибок
»
Любая
из
ошибок
программирования
,
которая
не
обнаруживается
на
этапах
компиляции
и
компоновки
программы
,
в
конечном
счете
может
проявиться
тремя
способами
:
привести
к
выдаче
системного
сообщения
об
ошибке
, «
зависанию
»
компьютера
и
получению
неверных
результатов
.
Однако
до
того
,
как
результат
работы
программы
становится
фатальным
,
ошибки
обычно
много
раз
проявляются
в
виде
неверных
промежуточных
результатов
,
неверных
управляющих
переменных
,
неверных
типах
данных
,
индексах
структур
данных
и
т
.
п
. (
рис
. 2.10).
А
это
значит
,
что
часть
ошибок
можно
попытаться
обнаружить
и
нейтрализовать
,
пока
они
еще
не
привели
к
тяжелым
последствиям
.
Программирование
,
при
котором
применяют
специальные
приемы
раннего
обнаружения
и
нейтрализации
ошибок
,
было
названо
защитным
или
программированием
с
защитой
от
ошибок
.
При
его
использовании
существенно
уменьшается
вероятность
получения
неверных
результатов
.
Детальный
анализ
ошибок
и
их
возможных
ранних
проявлений
показывает
,
что
целесообразно
проверять
:
•
правильность
выполнения
операций
ввода
-
вывода
;
•
допустимость
промежуточных
результатов
(
значений
управляющих
переменных
,
значений
индексов
,
типов
данных
,
значений
числовых
аргументов
и
т
.
д
.).
Проверки
правильности
выполнения
операций
ввода
-
вывода
.
Причинами
неверного
определения
исходных
данных
могут
являться
,
как
внутренние
ошибки
-
ошибки
устройств
ввода
-
вывода
или
программного
обеспечения
,
так
и
внешние
ошибки
-
ошибки
пользователя
.
При
этом
принято
различать
:
•
ошибки
передачи
-
аппаратные
средства
,
например
,
вследствие
неисправности
,
искажают
данные
;
ошибки
преобразования
-
программа
неверно
преобразует
исходные
данные
из
входного
формата
во
внутренний
;
•
ошибки
перезаписи
-
пользователь
ошибается
при
вводе
данных
,
например
,
вводит
лишний
или
другой
символ
;
•
ошибки
данных
-
пользователь
вводит
неверные
данные
.
Ошибки
передачи
обычно
контролируются
аппаратно
.
Для
защиты
от
ошибок
преобразования
данные
после
ввода
обычно
сразу
демонстрируют
пользователю
(«
эхо
»).
При
этом
выполняют
сначала
преобразование
во
внутренний
формат
,
а
затем
обратно
.
Однако
предотвратить
все
ошибки
преобразования
на
данном
этапе
обычно
крайне
сложно
,
поэтому
соответствующие
фрагменты
программы
тщательно
тестируют
[31],
используя