Ошибки при программировании делятся на

Существует три
основных типа ошибок в программах:

— ошибки этапа
компиляции (или синтаксические ошибки);

— ошибки этапа
выполнения или семантические ошибки);

— логические
ошибки.

Cинтаксические
ошибки происходят из-за нарушений
правил синтаксиса

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

ошибку, то отмечает
место (позицию или строку) ошибки и
выводт сообщение

об ошибке.

Наиболее
распространенными синтаксическими
ошибками являются:

— ошибки набора
(опечатки);

— пропущенные
точки с запятой;

— ссылки на
неописанные переменные;

— передача
неверного числа (или типа) параметров
процедуры или

функции;

— присваивание
переменной значений неверного типа.

После исправления
cинтаксической ошибки компиляцию можно
выполнить

заново. После
устранения всех синтаксических ошибок
и успешной компиля-

ции программа готова
к выполнению и поиску ошибок этапа
выполнения и ло-

гических ошибок.

Семантические
ошибки происходят, когда программа
компилируется, но

при выполнении
операторов что-то происходит неверно.
Например, программа

пытается открыть
для ввода несуществующий файл или
выполнить деление на

ноль. При обнаружении
семантических ошибок выполнение
программы заверша-

ется и выводится
сообщение об ошибке. Например, в системе
Turbo Pascal

выводится сообщение
следующего вида:

Run-time error ## at seg:ofs

По номеру
ошибки (##) можно установить причину ее
возникновения.

Логические ошибки
— это ошибки проектирования и реализации
програм-

мы. Логические
ошибки приводят к некорректному или
непредвиденному зна-

чению переменных,
неправильному виду графических
изображений или невы-

полнению кода, когда
это ожидается. Эти ошибки часто трудно
отслежива-

ются, поскольку ни
компилятор, ни исполняющая система не
обнаруживают их

автоматически, как
синтаксические и семантические ошибки.
Обычно системы

программирования
включает в себя средства отладки,
помогающие найти ло-

гические ошибки.

3.4.2. Цели и задачи отладки и тестирования.

Многие программисты
путают отладку программ с тестированием,
пред-

назначенным для
проверки их работоспособности. Отладка
имеет место тог-

да, когда программа
со всей очевидностью работает неправильно.
Поэтому

отладка начинается
всегда в предположении отказа программы.
Если же ока-

зывается, что
программа работает верно, то она
тестируется. Часто случа-

ется так, что после
прогона тестов программа вновь должна
быть подверг-

нута отладке. Таким
образом, тестирование устанавливает
факт наличия

ошибки, а отладка
выявляет ее причину, и эти два этапа
разработки прог-

раммы перекрываются.

3.4.3. Основные возможности интегрированного отладчика системы

программирования
Turbo Pascal.

Основной смысл
использования встроенного отладчика
состоит в управ-

ляемом выполнении
программы. Отслеживая выполнение
каждой инструкции,

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

ладчике предусмотрено
шесть основных механизмов управления
выполнением

программы, которые
позволяют:

— выполнять
инструкции по шагам(Run|Step Over или F8);

— трассировать
инструкции (Run|Trace Into или F7);

— выполнять
программы до позиции курсора (Run|Go to
Cursor или F4);

— выполнять
программу до заданной точки (Toggle
Breakpoint или

Ctrl+F8);

— находить
определенную точку (Search|Find Procedure…);

— выполнять сброс
программы (Run¦Reset Program или Ctrl+F2).

Выполнение
программы по шагам (команда Step Over меню
выполнения

Run) и трассировка
программы (команда Trace Into меню выполнения
Run)

дают возможность
построчного выполнения программы.
Единственное отличие

выполнения по шагам
и трассировки состоит в том, как они
работают с вы-

зовами процедур и
функций. Выполнение по шагам вызова
процедуры или

функции интерпретирует
вызов как простой оператор и после
завершения

подпрограммы
возвращает управление на следующую
строку. Трассировка

подпрограммы
загружает код этой подпрограммы и
продолжает ее построчное

выполнение.

Выполнение
программы до заданной точки (команда
Toggle Breakpoint

локального меню
редактора) — более гибкий механизм
отладки, чем исполь-

зование метода
выполнения до позиции курсора (команда
Go to Cursor меню

выполнения Run),
поскольку в программе можно установить
несколько точек

останова.

Интегрированная
среда разработки программы предусматривает
несколь-

ко способов поиска
в программе заданного места. Простейший
способ пре-

доставляет команда
Search|Find Procedure…, которая запрашивает
имя

процедуры или
функции, затем находит соответствующую
строку в файле, где

определяется эта
подпрограмма. Этот подход полезно
использовать при ре-

дактировании, но
его можно комбинировать с возможностью
выполнения прог-

раммы до определенной
точки, чтобы пройти программу до той
части кода,

которую надо отладить.

Чтобы сбрасить
все ранее задействованные отладочные
средства и

прекратитьт отладку
программы необходимо выполнить команду
Run|Program

reset или нажать клавиши
Ctrl+F2.

При выполнении
программы по шагам можно наблюдать ее
вывод несколь-

кими способами:

— переключение
в случае необходимости экранов
(Debug|User screen

или Alt+F5);

— открытие окна
вывода (Debug¦Output);

— использование
второго монитора;

Выполнение
программы по шагам или ее трассировка
могут помочь найти

ошибки в алгоритме
программы, но обычно желательно также
знать, что про-

исходит на каждом
шаге со значениями отдельных переменных.
Например, при

выполнении по шагам
цикла for полезно знать значение переменной
цикла.

Встроенный отладчик
имеет два инструментальных средства
для проверки со-

держимого переменных
программы:

— окно Watches
(Просмотр);

— диалоговое окно
Evaluate and Modify (Вычисление и модификация).

Чтобы открыть
окно Watches, необходимо выполнить
команду

Debug|Watch. Чтобы добавить
в окно Watches переменную, необходимо выпол-

нить
команду
Debug¦Watch¦Add Watch… или
нажать клавиши Ctrl+F7. Если

окно Watches является
активным окном, то можно добавить
выражение

просмотра, нажав
клавишу Ins. Отладчик открывает диалоговое
окно Add

Watch, запрашивающее
тип просматриваемого выражения. По
умолчанию выра-

жением считается
слово в позиции курсора в текущем окне
редактирования.

Просматриваемые
выражения, которые отслеживались ранее,
сохраняются в

списке протокола.
Последнее добавленное или модифицированное
просматри-

ваемое выражение
является текущим просматриваемым
выражением, которое

указывается выводимым
слева от него символом жирной левой
точки. Если

окно Watches активно,
можно удалить текущее выражение, нажав
клавишу Del

или Ctrl+Y. Чтобы
удалить все просматриваемые выражения,
необходимо вы-

полнить команду
Clear All локального меню активного окна
Watches. Чтобы

отредактировать
просматриваемое выражение, нужно
выполнить команду

Modify… или нажать
клавишу Enter локального меню активного
окна

Watches. Отладчик
открывает диалоговое окно Edit Watch,
аналогичное то-

му, которое
используется для добавления просматриваемого
выражения, ко-

торое позволяет
отредактировать текущее выражение.

Чтобы вычислить
выражение, необходимо выполнить
команду

Debug¦Evaluate/Modify…
или
нажать
клавиши
Ctrl+F4. Отладчик
открывает

диалоговое окно
Evaluate and Modify. По умолчанию слово в позиции
курсо-

ра в текущем окне
редактирования выводится подсвеченным
в поле

Expression. Можно
отредактировать это выражение, набрать
другое выраже-

ние или выбрать
вычисленное ранее выражение из списка
протокола.

Даже если не
установлены точки останова, можно выйти
в отладчик при

выполнении программы,
нажав клавиши Ctrl+Break. Отладчик находит
позицию

в исходном коде, где
прервалась программа. Затем, как и в
случае обычной

точки останова,
можно выполнить программу по шагам,
трассировать ее,

отследить или
вычислить выражения.

Иногда в ходе
отладки полезно узнать, как вы попали
в данную часть

кода. Окно Call Stack
показывает последовательность вызовов
процедур или

функций, которые
привели к текущему состоянию (глубиной
до 128 уровней).

Для вывода окна Call
Stack необходимо выполнить команду
Debug¦Call Stack

или нажать клавиши
Ctrl+F3.

13

Соседние файлы в папке 13_3xN

  • #
  • #
  • #

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

Определение

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

Баги обнаруживаются чаще всего в момент отладки или бета-тестирования. Реже – после итогового релиза готовой программы. Вот несколько вариантов багов:

  1. Появляется сообщение об ошибке, но приложение продолжает функционировать.
  2. ПО вылетает или зависает. Никаких предупреждений или предпосылок этому не было. Процедура осуществляется неожиданно для пользователя. Возможен вариант, при котором контент перезапускается самостоятельно и непредсказуемо.
  3. Одно из событий, описанных ранее, сопровождается отправкой отчетов разработчикам.

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

История происхождения термина

Баг – слово, которое используется разработчиками в качестве сленга. Оно произошло от слова «bug» – «жук». Точно неизвестно, откуда в программировании и IT возник соответствующий термин. Существуют две теории:

  1. 9 сентября 1945 года ученые из Гарварда тестировали очередную вычислительную машину. Она называлась Mark II Aiken Relay Calculator. Устройство начало работать с ошибками. Когда его разобрали, то ученые заметили мотылька, застрявшего между реле. Тогда некая Грейс Хоппер назвала произошедший сбой упомянутым термином.
  2. Слово «баг» появилось задолго до появления Mark II. Термин использовался Томасом Эдисоном и указывал на мелкие недочеты и трудности. Во время Второй Мировой войны «bugs» называли проблемы с радарной электроникой.

Второй вариант кажется более реалистичным. Это факт, который подтвержден документально. Со временем научились различать различные типы багов в IT. Далее они будут рассмотрены более подробно.

Как классифицируют

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

  1. Серьезные неполадки. Это нарушения работоспособности приложения, которые могут приводить к непредвиденным крупным изменениям.
  2. Незначительные ошибки в программах. Чаще всего не оказывают серьезного воздействия на функциональность ПО.
  3. Showstopper. Критические проблемы в приложении или аппаратном обеспечении. Приводят к выходу программы из строя почти всегда. Для примера можно взять любое клиент-серверное приложение, в котором не получается авторизоваться через логин и пароль.

Последний вариант требует особого внимания со стороны программистов. Их стараются обнаружить и устранить в первую очередь. Критические ошибки могут отложить релиз исходной программы на неопределенный срок.

Также существуют различные виды сбоев в плане частоты проявления: постоянные и «разовые». Вторые встречаются редко, чаще – при определенных настройках и действиях со стороны пользователя. Первые появляются независимо от используемой платформы и выполненных клиентом манипуляций.

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

Виды

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

Разработчики выделяют следующие типы ошибок по уровню сложности:

  1. «Борбаг» – «стабильная» неполадка. Она легко обнаруживается на этапе разработки и компилирования. Иногда – во время тестирования наработкой исходной программы.
  2. «Гейзенбаг» – баги с поддержкой изменения свойств, включая зависимость от среды, в которой было запущено приложение. Сюда относят периодические неполадки в программах. Они могут исчезать на некоторое время, но через какой-то промежуток вновь дают о себе знать.
  3. «Мандельбаг» – непредвиденные ошибки. Обладают энтропийным поведением. Предсказать, к чему они приведут, практически невозможно.
  4. «Шрединбаг» – критические неполадки. Приводят к тому, что злоумышленники могут взломать программу. Данный тип ошибок обнаружить достаточно трудно, потому что они никак себя не проявляют.

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

Типы багов

Ошибки в программах бывают:

  • логическими;
  • синтаксическими;
  • взаимодействия;
  • компиляционные;
  • ресурсные;
  • арифметические;
  • среды выполнения.

Это – основная классификация сбоев в приложениях и операционных системах. Логические, синтаксические и «среды выполнения» встречаются в разработке чаще остальных. На них будет сделан основной акцент.

Ошибки синтаксиса

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

Синтаксические ошибки – ошибки синтаксиса, правил языка. Вот пример в Паскале:

Код написан неверно. Согласно действующим синтаксическим нормам, в Pascal в первой строчке нужно в конце поставить точку с запятой.

Логические

Тут стоит выделить обычные и арифметические типы. Вторые возникают, когда программе при работе необходимо вычислить много переменных, но на каком-то этапе расчетов возникают неполадки или нечто непредвиденное. Пример – получение в результатах «бесконечности».

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

Выше – пример логической ошибки в программе. Тут:

  1. Происходит сравнение значения i с 15.
  2. На экран выводится сообщение, если I = 15.
  3. В заданном цикле i не будет равно 15. Связано это с диапазоном значений – от 1 до 10.

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

Время выполнения

Run-time сбои – это ошибка времени выполнения программы. Встречается даже когда исходный код лишен логических и синтаксических ошибок. Связаны такие неполадки с ходом выполнения программного продукта. Пример – в процессе функционирования ПО был удален файл, считываемый программой. Если игнорировать подобные неполадки, можно столкнуться с аварийным завершением работы контента.

Самый распространенный пример в данной категории – это неожиданное деление на ноль. Предложенный фрагмент кода с точки зрения синтаксиса и логики написан грамотно. Но, если клиент наберет 0, произойдет сбой системы.

Компиляционный тип

Встречается при разработке на языках высокого уровня. Во время преобразований в машинный тип «что-то идет не так». Причиной служат синтаксические ошибки или сбои непосредственно в компиляторе.

Наличие подобных неполадок делает бета-тестирование невозможным. Компиляционные ошибки устраняются при разработке-отладке.

Ресурсные

Ресурсный тип ошибок – это сбои вроде «переполнение буфера» или «нехватка памяти». Тесно связаны с «железом» устройства. Могут быть вызваны действиями пользователя. Пример – запуск «свежих» игр на стареньких компьютерах.

Исправить ситуацию помогают основательные работы над исходным кодом. А именно – полное переписывание программы или «проблемного» фрагмента.

Взаимодействие

Подразумевается взаимодействие с аппаратным или программным окружением. Пример – ошибка при использовании веб-протоколов. Это приведет к тому, что облачный сервис не будет нормально функционировать. При постоянном возникновении соответствующей неполадки остается один путь – полностью переписывать «проблемный» участок кода, ответственный за соответствующий баг.

Исключения и как избежать багов

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

Исключения бывают:

  1. Программными. Они генерируются приложением или ОС.
  2. Аппаратными. Создаются процессором. Пример – обращение к невыделенной памяти.

Исключения нужны для охвата критических багов. Избежать неполадок помогут отладчики на этапе разработки. А еще – своевременное поэтапное тестирование программы.

P. S. Большой выбор курсов по тестированию есть и в Otus. Присутствуют варианты как для продвинутых, так и для начинающих пользователей.

Существует две фундаментальные стратегии: обработка исправимых ошибок (исключения, коды возврата по ошибке, функции-обработчики) и неисправимых (assert(), abort()). В каких случаях какую стратегию лучше использовать?

Виды ошибок

Ошибки возникают по разным причинам: пользователь ввёл странные данные, ОС не может дать вам обработчика файла или код разыменовывает (dereferences) nullptr. Каждая из описанных ошибок требует к себе отдельного подхода. По причинам ошибки делятся на три основные категории:

  • Пользовательские ошибки: здесь под пользователем подразумевается человек, сидящий перед компьютером и действительно «использующий» программу, а не какой-то программист, дёргающий ваш API. Такие ошибки возникают тогда, когда пользователь делает что-то неправильно.
  • Системные ошибки появляются, когда ОС не может выполнить ваш запрос. Иными словами, причина системных ошибок — сбой вызова системного API. Некоторые возникают потому, что программист передал системному вызову плохие параметры, так что это скорее программистская ошибка, а не системная.
  • Программистские ошибки случаются, когда программист не учитывает предварительные условия API или языка программирования. Если API требует, чтобы вы не вызывали foo() с 0 в качестве первого параметра, а вы это сделали, — виноват программист. Если пользователь ввёл 0, который был передан foo(), а программист не написал проверку вводимых данных, то это опять же его вина.

Каждая из описанных категорий ошибок требует особого подхода к их обработке.

Пользовательские ошибки

Сделаю очень громкое заявление: такие ошибки — на самом деле не ошибки.

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

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

Конечно, такое не всегда возможно. Иногда проверять вводимые данные слишком дорого, иногда это не позволяет сделать архитектура кода или разделение ответственности. Но в таких случаях ошибки должны обрабатываться однозначно как исправимые. Иначе, допустим, ваша офисная программа будет падать из-за того, что вы нажали backspace в пустом документе, или ваша игра станет вылетать при попытке выстрелить из разряженного оружия.

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

Системные ошибки

Обычно системные ошибки нельзя предсказать. Более того, они недетерминистские и могут возникать в программах, которые до этого работали без нареканий. В отличие от пользовательских ошибок, зависящих исключительно от вводимых данных, системные ошибки — настоящие ошибки.

Но как их обрабатывать, как исправимые или неисправимые?

Это зависит от обстоятельств.

Многие считают, что ошибка нехватки памяти — неисправимая. Зачастую не хватает памяти даже для обработки этой ошибки! И тогда приходится просто сразу же прерывать выполнение.

Но падение программы из-за того, что ОС не может выделить сокет, — это не слишком дружелюбное поведение. Так что лучше бросить исключение и позволить catch аккуратно закрыть программу.

Но бросание исключения — не всегда правильный выбор.

Кто-то даже скажет, что он всегда неправильный.

Если вы хотите повторить операцию после её сбоя, то обёртывание функции в try-catch в цикле — медленное решение. Правильный выбор — возврат кода ошибки и цикличное исполнение, пока не будет возвращено правильное значение.

Если вы создаёте вызов API только для себя, то просто выберите подходящий для своей ситуации путь и следуйте ему. Но если вы пишете библиотеку, то не знаете, чего хотят пользователи. Дальше мы разберём подходящую стратегию для этого случая. Для потенциально неисправимых ошибок подойдёт «обработчик ошибок», а при других ошибках необходимо предоставить два варианта развития событий.

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

Программистские ошибки

Это худший вид ошибок. Для их обработки я стараюсь сделать так, чтобы мои ошибки были связаны только с вызовами функций, то есть с плохими параметрами. Прочие типы программистских ошибок могут быть пойманы только в runtime, с помощью отладочных макросов (assertion macros), раскиданных по коду.

При работе с плохими параметрами есть две стратегии: дать им определённое или неопределённое поведение.

Если исходное требование для функции — запрет на передачу ей плохих параметров, то, если их передать, это считается неопределённым поведением и должно проверяться не самой функцией, а оператором вызова (caller). Функция должна делать только отладочное подтверждение (debug assertion).

С другой стороны, если отсутствие плохих параметров не является частью исходных требований, а документация определяет, что функция будет бросать bad_parameter_exception при передаче ей плохого параметра, то передача — это хорошо определённое поведение (бросание исключения или любая другая стратегия обработки исправимых ошибок), и функция всегда должна это проверять.

В качестве примера рассмотрим получающие функции (accessor functions) std::vector<T>: в спецификации на operator[] говорится, что индекс должен быть в пределах валидного диапазона, при этом at() сообщает нам, что функция кинет исключение, если индекс не попадает в диапазон. Более того, большинство реализаций стандартных библиотек обеспечивают режим отладки, в котором проверяется индекс operator[], но технически это неопределённое поведение, оно не обязано проверяться.

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

Когда нужно проверять только с помощью отладочных подтверждений, а когда — постоянно?

К сожалению, однозначного рецепта нет, решение зависит от конкретной ситуации. У меня есть лишь одно проверенное правило, которому я следую при разработке API. Оно основано на наблюдении, что проверять исходные условия должен вызывающий, а не вызываемый. А значит, условие должно быть «проверяемым» для вызывающего. Также условие «проверяемое», если можно легко выполнить операцию, при которой значение параметра всегда будет правильным. Если для параметра это возможно, то это получается исходное условие, а значит, проверяется только посредством отладочного подтверждения (а если слишком дорого, то вообще не проверяется).

Но конечное решение зависит от многих других факторов, так что очень трудно дать какой-то общий совет. По умолчанию я стараюсь свести к неопределённому поведению и использованию только подтверждений. Иногда бывает целесообразно обеспечить оба варианта, как это делает стандартная библиотека с operator[] и at().

Хотя в ряде случаев это может быть ошибкой.

Об иерархии std::exception

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

Я предлагаю наследовать только от одного из этих четырёх классов:

  • std::bad_alloc: для сбоев выделения памяти.
  • std::runtime_error: для общих runtime-ошибок.
  • std::system_error (производное от std::runtime_error): для системных ошибок с кодами ошибок.
  • std::logic_error: для программистских ошибок с определённым поведением.

Обратите внимание, что в стандартной библиотеке разделяются логические (то есть программистские) и runtime-ошибки. Runtime-ошибки — более широкое определение, чем «системные». Оно описывает «ошибки, обнаруживаемые только при выполнении программы». Такая формулировка не слишком информативна. Лично я использую её для плохих параметров, которые не являются исключительно программистскими ошибками, а могут возникнуть и по вине пользователей. Но это можно определить лишь глубоко в стеке вызовов. Например, плохое форматирование комментариев в standardese приводит к исключению при парсинге, проистекающему из std::runtime_error. Позднее оно ловится на соответствующем уровне и фиксируется в логе. Но я не стал бы использовать этот класс иначе, как и std::logic_error.

Подведём итоги

Есть два пути обработки ошибок:

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

Подтверждения — это особый вид стратегии обработки неисправимых ошибок, только в режиме отладки.

Есть три основных источника ошибок, каждый требует особого подхода:

  • Пользовательские ошибки не должны обрабатываться как ошибки на верхних уровнях программы. Всё, что вводит пользователь, должно проверяться соответствующим образом. Это может обрабатываться как ошибки только на нижних уровнях, которые не взаимодействуют с пользователями напрямую. Применяется стратегия обработки исправимых ошибок.
  • Системные ошибки могут обрабатываться в рамках любой из двух стратегий, в зависимости от типа и тяжести. Библиотеки должны работать как можно гибче.
  • Программистские ошибки, то есть плохие параметры, могут быть запрещены исходными условиями. В этом случае функция должна использовать только проверку с помощью отладочных подтверждений. Если же речь идёт о полностью определённом поведении, то функции следует предписанным образом сообщать об ошибке. Я стараюсь по умолчанию следовать сценарию с неопределённым поведением и определяю для функции проверку параметров лишь тогда, когда это слишком трудно сделать на стороне вызывающего.

Гибкие методики обработки ошибок в C++

Иногда что-то не работает. Пользователи вводят данные в недопустимом формате, файл не обнаруживается, сетевое соединение сбоит, в системе кончается память. Всё это ошибки, и их надо обрабатывать.

Это относительно легко сделать в высокоуровневых функциях. Вы точно знаете, почему что-то пошло не так, и можете обработать это соответствующим образом. Но в случае с низкоуровневыми функциями всё не так просто. Они не знают, что пошло не так, они знают лишь о самом факте сбоя и должны сообщить об этом тому, кто их вызвал.

В C++ есть два основных подхода: коды возврата ошибок и исключения. Сегодня широко распространено использование исключений. Но некоторые не могут / думают, что не могут / не хотят их использовать — по разным причинам.

Я не буду принимать чью-либо сторону. Вместо этого я опишу методики, которые удовлетворят сторонников обоих подходов. Особенно методики пригодятся разработчикам библиотек.

Проблема

Я работаю над проектом foonathan/memory. Это решение предоставляет различные классы выделения памяти (allocator classes), так что в качестве примера рассмотрим структуру функции выделения.

Для простоты возьмём malloc(). Она возвращает указатель на выделяемую память. Если выделить память не получается, то возвращается nullptr, то есть NULL, то есть ошибочное значение.

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

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

Это можно расценить как недостаток.

Но в подобных ситуациях исключения имеют также очень большое преимущество: функция выделения памяти либо возвращает валидную память, либо вообще ничего не возвращает. Это функция «всё или ничего», возвращаемое значение всегда будет валидным. Это полезное следствие согласно принципу Скотта Майера «Make interfaces hard to use incorrectly and easy to use correctly».

Учитывая вышесказанное, можно утверждать, что вам следует использовать исключения в качестве механизма обработки ошибок. Этого мнения придерживается большинство разработчиков на С++, включая и меня. Но проект, которым я занимаюсь, — это библиотека, предоставляющая средства выделения памяти, и предназначена она для приложений, работающих в реальном времени. Для большинства разработчиков подобных приложений (особенно для игроделов) само использование исключений — исключение.

Каламбур детектед.

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

Так что же делать?

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

К счастью, я могу определить, что вы делаете, когда обнаруживаете ошибку нехватки памяти: чаще всего вы журналируете это событие и прерываете программу, поскольку она не может корректно работать без памяти. В таких ситуациях исключения — просто способ передачи контроля другой части кода, которая журналирует и прерывает программу. Но есть старый и эффективный способ передачи контроля: указатель функции (function pointer), то есть функция-обработчик (handler function).

Если у вас включены исключения, то вы просто их бросаете. В противном случае вызываете функцию-обработчика и затем прерываете программу. Это предотвратит бесполезную работу функции-обработчика, та позволит программе продолжить выполняться в обычном режиме. Если не прервать, то произойдёт нарушение обязательного постусловия функции: всегда возвращать валидный указатель. Ведь на выполнении этого условия может быть построена работа другого кода, да и вообще это нормальное поведение.

Я называю такой подход обработкой исключений и придерживаюсь его при работе с памятью.

Решение 1: обработчик исключений

Если вам нужно обработать ошибку в условиях, когда наиболее распространённым поведением будет «журналировать и прервать», то можно использовать обработчика исключений. Это такая функция-обработчик, которая вызывается вместо бросания объекта-исключения. Её довольно легко реализовать даже в уже существующем коде. Для этого нужно поместить управление обработкой в класс исключений и обернуть в макрос выражение throw.

Сначала дополним класс и добавим функции для настройки и, возможно, запрашивания функции-обработчика. Я предлагаю делать это так же, как стандартная библиотека обрабатывает std::new_handler:

class my_fatal_error
{
public:
    // тип обработчика, он должен брать те же параметры, что и конструктор,
    // чтобы у них была одинаковая информация
    using handler = void(*)( ... );

    // меняет функцию-обработчика
    handler set_handler(handler h);

    // возвращает текущего обработчика
    handler get_handler();

    ... // нормальное исключение
};

Поскольку это входит в область видимости класса исключений, вам не нужно именовать каким-то особым образом. Отлично, нам же легче.

Если исключения включены, то для удаления обработчика можно использовать условное компилирование (conditional compilation). Если хотите, то также напишите обычный подмешанный класс (mixin class), дающий требуемую функциональность.

Конструктор исключений элегантен: он вызывает текущую функцию-обработчика, передавая ей требуемые аргументы из своих параметров. А затем комбинирует с последующим макросом throw:

If```cpp #if EXCEPTIONS #define THROW(Ex) throw (Ex) #else #define THROW(Ex) (Ex), std::abort() #endif
> Такой макрос throw также предоставляется [foonathan/compatiblity](https://github.com/foonathan/compatibility).

Можно использовать его и так:

```cpp
THROW(my_fatal_error(...))

Если у вас включена поддержка исключений, то будет создан и брошен объект-исключение, всё как обычно. Но если поддержка выключена, то объект-исключение всё равно будет создан, и — это важно — только после этого произойдёт вызов std::abort(). А поскольку конструктор вызывает функцию-обработчика, то он и работает, как требуется: вы получаете точку настройки для журналирования ошибки. Благодаря же вызову std::abort() после конструктора пользователь не может нарушить постусловие.

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

Так что при этой методике вам ещё будет доступна определённая степень кастомизации, даже если вы отключите исключения. Конечно, замена неполноценная, мы только журналируем и прерываем работу программы, без дальнейшего продолжения. Но в ряде случаев, в том числе при исчерпании памяти, это вполне пригодное решение.

А если я хочу продолжить работу после бросания исключения?

Методика с обработчиком исключений не позволяет этого сделать в связи с постусловием кода. Как же тогда продолжить работу?

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

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

Для примера снова возьмём функцию выделения памяти. В этом случае я использую такие функции:

void* try_malloc(..., int &error_code) noexcept;

void* malloc(...);

При сбое выделения памяти первая версия возвращает nullptr и устанавливает error_code в коде ошибки. Вторая версия не возвращает nullptr, зато бросает исключение. Обратите внимание, что в рамках первой версии очень легко реализовать вторую:

void* malloc(...)
{
    auto error_code = 0;
    auto res = try_malloc(..., error_code);
    if (!res)
        throw malloc_error(error_code);
    return res;
}

Не делайте этого в обратной последовательности, иначе вам придётся ловить исключение, а это дорого. Также это не даст нам скомпилировать код без включённой поддержки исключений. Если сделаете, как показано, то можете просто стереть другую перегрузку (overload) с помощью условного компилирования.

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

Решение 2: предоставить две перегрузки

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

Если рассматриваемая функция не имеет возвращаемого значения, то можете её использовать для кода ошибки. В противном случае вам придётся возвращать недопустимое значение для сигнализирования об ошибке — как nullptr в вышеприведённом примере, — а также установить выходной параметр для кода ошибки, если хотите предоставить вызывающему дополнительную информацию.

Пожалуйста, не используйте глобальную переменную errno или что-то типа GetLastError()!

Если возвращаемое значение не содержит недопустимое значение для обозначения сбоя, то по мере возможности используйте std::optional или что-то похожее.

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

std::system_error

Подобная система идеально подходит для работы с кодами ошибок в С++ 11.

Она возвращает непортируемый (non-portable) код ошибки std::error_code, то есть возвращаемый функцией операционной системы. С помощью сложной системы библиотечных средств и категорий ошибок вы можете добавить собственные коды ошибок, или портируемые std::error_condition. Для начала почитайте об этом здесь. Если нужно, то можете использовать в функции кода ошибки std::error_code. А для функции исключения есть подходящий класс исключения: std::system_error. Он берёт std::error_code и применяется для передачи этих ошибок в виде исключений.

Эту или подобную систему должны использовать все низкоуровневые функции, являющиеся закрытыми обёртками ОС-функций. Это хорошая — хотя и сложная — альтернатива службе кодов ошибок, предоставляемой операционной системой.

Да, и мне ещё нужно добавить подобное в функции виртуальной памяти. На сегодняшний день они не предоставляют коды ошибок.

std::expected

Выше упоминалось о проблеме, когда у вас нет возвращаемого значения, содержащего недопустимое значение, которое можно использовать для сигнализирования об ошибке. Более того, выходной параметр — не лучший способ получения кода ошибки.

А глобальные переменные вообще не вариант!

В № 4109 предложено решение: std::expected. Это шаблон класса, который также хранит возвращаемое значение или код ошибки. В вышеприведённом примере он мог бы использоваться так:

std::expected<void*, std::error_code> try_malloc(...);

В случае успеха std::expected будет хранить не-null указатель памяти, а при сбое — std::error_code. Сейчас эта методика работает при любых возвращаемых значениях. Комбинация std::expected и функции исключения определённо допускает любые варианты использования.

Заключение

Если вы создаёте библиотеки, то иногда приходится обеспечивать максимальную гибкость использования. Под этим подразумевается и разнообразие средств обработки ошибок: иногда требуются коды возврата, иногда — исключения.

Одна из возможных стратегий — улаживание этих противоречий с помощью обработчика исключений. Просто удостоверьтесь, что когда нужно, то вызывается callback, а не бросается исключение. Это замена для критических ошибок, которая в любом случае будет журналироваться перед прерыванием работы программы. Как таковой этот способ не универсален, вы не можете переключаться в одной программе между двумя версиями. Это лишь обходное решение при отключённой поддержке исключений.

Более гибкий подход — просто предоставить две перегрузки, одну с исключениями, а вторую без. Это даст пользователям максимальную свободу, они смогут выбирать ту версию, что лучше подходит в их ситуации. Недостаток этого подхода: вам придётся больше потрудиться при создании библиотеки.

#Руководства

  • 30 июн 2020

  • 14

Что такое баги, ворнинги и исключения в программировании

Разбираемся, какие бывают типы ошибок в программировании и как с ними справляться.

 vlada_maestro / shutterstock

Евгений Кучерявый

Пишет о программировании, в свободное время создаёт игры. Мечтает открыть свою студию и выпускать ламповые RPG.

Многим известно слово баг (англ. bug — жук), которым называют ошибки в программах. Однако баг — это не совсем ошибка, а скорее неожиданный результат работы. Также есть и другие термины: ворнинг, исключение, утечка.

В этой статье мы на примере C++ разберём, что же значат все эти слова и как эти проблемы влияют на эффективность программы.

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

//В конце команды забыли поставить точку с запятой (;)
int a = 5

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

Также существуют ворнинги (англ. warning предупреждение). Они не являются ошибками, поэтому программа всё равно будет собрана. Вот пример:

int main()
{
   //Мы создаём две переменные, которые просто занимают память и никак не используются
   int a, b;
}

Мы можем попросить компилятор показать нам все предупреждения с помощью флага -Wall:

Предупреждения не являются чем-то критичным, но могут иметь негативные последствия. Например, ваша программа будет использовать больше памяти, чем должна. Так как C++ нужен в том числе и для разработки высоконагруженных систем, этого допускать нельзя.

После восклицательного знака в треугольнике количество предупреждений

Третий вид ошибок — ошибки сегментации (англ. segmentation fault, сокр. segfault, жарг. сегфолт). Они возникают, если программа пытается записать что-то в ячейку, недоступную для записи. Например:

//Создаём константный массив символов 
const char * s = "Hello World";
//Если мы попытаемся перезаписать значение константы, компилятор выдаст ошибку
//Но с помощью указателей мы можем обойти её, поэтому программа успешно скомпилируется
//Однако во время работы она будет выдавать ошибку сегментации
* (char *) s = 'H';

Вот результат работы такого кода:

Мы выяснили, что баг — это не совсем ошибка, а скорее неожиданное поведение программы или результат такого поведения. Баги могут быть чем-то забавным или неприятным. Например, как в играх:

Но они могут привести и к более серьёзным последствиям. Если неправильно спроектировать работу многопоточного приложения, то потоки будут постоянно опережать друг друга. Например, сообщение об ошибке из одного потока может опоздать на миллисекунду, из-за чего второй поток подумает, что никакой ошибки не было, и продолжит работу.

Если ваш код приводит в действие какое-нибудь потенциально опасное устройство, то ценой такой ошибки может быть чья-нибудь жизнь. Такое случилось с кодом для аппарата лучевой терапии Therac-25 — как минимум два человека умерло и ещё больше пострадали из-за превышения дозы радиации.

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

Конвертировать введённое значение не всегда возможно, поэтому функция, которая занимается преобразованием, «выбрасывает» исключение (англ. exception). Это специальное сообщение говорит о том, что что-то идёт не так.

Если разработчик не описывает логику работы программы при вы выбрасывании исключения, то программа аварийно закрывается. Подробнее мы рассказали об этом в статье про ввод и конвертацию в C++.

Одно из самых известных исключений — переполнение стека (англ. stack overflow). В честь него даже назвали сайт, на котором программисты ищут помощь в решении своих проблем.

int main()
{
   //Бесконечная рекурсия - одна из причин переполнения стека вызовов
   main();
}

Компилятор C++ при этом может выдать ошибку сегментации, а не сообщение о переполнении стека:

Вот аналогичный код на языке C#:

class Program
{
   static void Main(string[] args)
   {
       Main(args);
   }
}

Однако сообщение в этот раз более конкретное:

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

Похожая ситуация — переполнение буфера (англ. buffer overflow). Она происходит, когда записываемое значение больше выделенной области в памяти.

//Пробуем записать в переменную типа int значение, которое превышает лимит
//Константа INT_MAX находится в библиотеке climits
int a = INT_MAX + 1;

Обратите внимание, что мы получили предупреждение об арифметическом переполнении (англ. integer overflow):

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

Арифметическое переполнение стало причиной одной из самых дорогих аварий, произошедших из-за ошибки в коде. В 1996 году ракета-носитель «Ариан-5» взорвалась на 40-й секунде полёта — потери оценивают в 360–500 миллионов долларов.

К сожалению, вручную всё это заметить и исправить не получится. Однако существуют различные инструменты и технологии, которые могут помочь.

Один из таких инструментов — отладчик. Он помогает контролировать ход работы программы, чтобы отслеживать разные показатели.

Второй, более эффективный метод — unit-тесты. Они представляют из себя набор описанных ситуаций для каждого компонента программы с указанием ожидаемого поведения.

Например, у вас есть функция sum (int a, int b), которая возвращает сумму двух чисел. Вы можете написать unit-тесты, чтобы проверять следующие ситуации:

Входные данные Ожидаемый результат
5, 10 15
99, 99 198
8, -9 -1
-1, -1 -2
fff, 8 IllegalArgumentException

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

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


Учись бесплатно:
вебинары по программированию, маркетингу и дизайну.

Участвовать

Школа дронов для всех
Учим программировать беспилотники и управлять ими.

Узнать больше

Подробности
июля 04, 2014
Просмотров: 133156

Виды ошибок в программировании

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

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

Если вы абсолютный новичок в области программирования то эта статья непременно будет вам интересна: Основы программирования для начинающих.

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

Основные виды ошибок в программировании

Компьютерное программирование это огромное поле с сотнями языков, которые используют миллионы приложений. Это программирование операционной системы, прикладное программирование, встроенное кодирование системы, веб-разработка, приложения для мобильных платформ, развитие программ, развернутых в интернете, научные вычисления. В таблице представлены основные виды ошибок.

 

Тип ошибок программирования

                              Описание

Логическая ошибка

Это, пожалуй, наиболее серьезная из всех ошибок. Когда написанная программа на любом языке компилирует и работает правильно, но выдает неправильный вывод, недостаток заключается в логике основного программирования. Это ошибка, которая была унаследована от недостатка в базовом алгоритме. Сама логика, на которой базируется вся программа, является ущербной. Чтобы найти решение такой ошибки нужно фундаментальное изменение алгоритма. Вам нужно начать копать в алгоритмическом уровне, чтобы сузить область поиска такой ошибки.

Синтаксическая ошибка

Каждый компьютерный язык, такой как C, Java, Perl и Python имеет специфический синтаксис, в котором будет написан код. Когда программист не придерживаться «грамматики» спецификациями компьютерного языка, возникнет ошибка синтаксиса. Такого рода ошибки легко устраняются на этапе компиляции.

Ошибка компиляции

Компиляция это процесс, в котором программа, написанная на языке высокого уровня, преобразуется в машиночитаемую форму. Многие виды ошибок могут происходить на этом этапе, в том числе и синтаксические ошибки. Иногда, синтаксис исходного кода может быть безупречным, но ошибка компиляции все же может произойти. Это может быть связано с проблемами в самом компиляторе. Эти ошибки исправляются на стадии разработки.

Ошибки среды выполнения (RunTime)

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

Арифметическая ошибка

Многие программы используют числовые переменные, и алгоритм может включать несколько математических вычислений. Арифметические ошибки возникают, когда компьютер не может справиться с проблемами, такими как «Деление на ноль», или ведущие к бесконечному результату. Это снова логическая ошибка, которая может быть исправлена  только путем изменения алгоритма.

Ошибки ресурса

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

Ошибка взаимодействия

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

Интенсивное тестирование и фаза отладки неотъемлемая часть цикла разработки программного обеспечения, которое может помочь пресечь эти ошибки в зародыше, прежде чем произойдет полномасштабное развертывание программного обеспечения. Много ошибок можно избежать с помощью предварительного планирования во время стадии кодирования. Большинство ошибок можно исправить в процессе разработки программного обеспечения через практику и строгие процедуры отладки. Ошибки являются частью обучения, и их никогда нельзя полностью избежать, Тем не менее, у вас могут появляться новые ошибки, но повторять старые вы не должны!

Читайте также

Виды ошибок в программах

Я учусь на своих ошибках. Ругаю себя за это, но продолжаю ошибаться. С другой стороны — это всё-таки лучше, чем не учиться совсем, и наступать на одни и те же грабли бесконечно.

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

Итак, основных вида всего три:

  • Синтаксические ошибки
  • Логические ошибки
  • Ошибки выполнения программы

Синтаксические ошибки в программах

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

Синтаксические ошибки — это ошибки синтаксиса (а то бы вы не догадались))). То есть ошибки правил языка. Например, для Паскаля это будет синтаксической ошибкой:

WriteLn('Hello, World !!!')
ReadLn;

Потому что после первой строки нет точки с запятой.

Подобные ошибки очень часто совершают новички. И это вгоняет их в ступор — они пугаются и не могут понять, что же не так с их кодом. Хотя если бы они внимательно его посмотрели и прочитали сообщение об ошибке, то легко могли бы исправить её:

Синтаксические ошибки в программах

Потому что в сообщении чётко сказано:

Syntax error, ";" expected but "identifier READLN" found

что можно перевести как

синтаксическая ошибка, ";" ожидалось, но "READLN" найден

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

Логические ошибки в программах

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

И компилятор вам ничего об этой ошибке не расскажет, потому что правила языка не нарушены.

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

Пример логической ошибки:

for i := 1 to 10 do
  if i = 15 then WriteLn('i = 15');

Здесь мы сравниваем значение i с числом 15, и выводим сообщение, если i = 15.
Но фишка в том, что в данном цикле i не будет равно 15 НИКОГДА, потому что в цикле переменной i присваиваются значения от 1 до 10.

Эта ошибка довольно безобидная. Здесь мы имеем просто бессмысленный код, который не причинит никакого вреда.
Однако представьте, что программа должна выдавать какой-то сигнал тревоги, если i = 15. Тогда получится, что никакого сигнала пользователь никогда не услышит, даже если случилось что-то страшное. А всё потому, что программист немного ошибся. Вот так вот и падают ракеты и самолёты…

Распространённые логические ошибки в С++ вы можете посмотреть здесь.

Ошибки времени выполнения программы

Даже если исходный код не содержит ни логических, не синтаксических ошибок, это ещё не означает, что ваша программа безупречна. Потому что ошибки могут возникнуть в ходе выполнения программы. Например, случайно будет удалён файл, который должна читать программа, и она не сможет его найти. Если не принять мер, то программа может завершиться аварийно. А пользователям такое поведение программ очень не нравится.

Одна из самых рапространённых ошибок времени выполнения — это неожиданное деление на ноль. Пример:

Write('y = ');
ReadLn(y);
x := 100 / y;
WriteLn('100 / ', y, ' = ', x);

Что здесь такого? Всё правильно и с точки зрения логики, и с точки зрения синтаксиса. И в большинстве случаев программа отработает без каких-либо неожиданностей.

Но представьте, что пользователь введёт ноль. Что тогда будет? Правильно — попытка деления на ноль. А на ноль делить нельзя. Поэтому во время выполнения этой программы произойдёт ошибка, которая очень расстроит пользователя. Потому что в случае, например, с консольным приложением программа просто закроется, и пользователь не поймёт, что это было. Но зато поймёт, что программа — говно, и программы от этого разработчика лучше больше никогда не использовать.

В данном случае, если вы не уверены на 100%, что y будет отличаться от нуля, надо всегда делать проверку на ноль. И хороший код должен быть хотя бы таким:

Write('y = ');
ReadLn(y);
if y = 0 then WriteLn('ERROR: y = 0')
else
  begin
    x := 100 / y;
    WriteLn('100 / ', y, ' = ', x);
  end;

Ну что же. На этом с видами ошибок пока всё. Изучайте программирование и поменьше ошибайтесь.

Основы программирования

Основы программирования
Каждый профессионал когда-то был чайником. Наверняка вам знакомо состояние, когда “не знаешь как начать думать, чтобы до такого додуматься”. Наверняка вы сталкивались с ситуацией, когда вы просто не знаете, с чего начать.
Эта книга ориентирована как раз на таких людей, кто хотел бы стать программистом, но совершенно не знает, как начать этот путь.
Подробнее…

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

Что такое баг?

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

Варианты ошибок:

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

Сложнее всего работать с компьютерными играми, в которых чаще используют термин «краш» (crash). Он означает критическую проблему при запуске или использовании программы. Когда говорят о багах, то чаще имеют в виду сбои графики, например, если игрок «проваливается в текстуры».

Комьюнити теперь в Телеграм

Подпишитесь и будьте в курсе последних IT-новостей

Подписаться

Классификация багов

Точка зрения пользователей часто не совпадает с мнением программистов. Так, для первых всего лишь произошел сбой, «приложение перестало работать». Кодеру же предстоит головная боль с определением источника проблемы. Ведь ошибка в программе, вероятно, проявляется лишь на конкретном железе или при сочетании с другим софтом (часто с антивирусами).

Ошибка программы

Баги делят на категории в зависимости от их критичности:

  1. незначительные ошибки,
  2. серьезные ошибки,
  3. showstopper.

Последние указывают на критическую программную или аппаратную проблему, из-за которой ПО теряет свою функциональность практически на 100%. Например, не удается авторизоваться через логин-пароль или перестала работать кнопка «Далее». Поэтому таким ошибкам отдают приоритет.

Также есть деление ошибок по частоте проявления. Проще всего исправлять постоянные, возникающие при одних и тех же обстоятельствах, независимо от платформы, аппаратной части компьютера или каких-то действий пользователя. Сложность возрастает при периодических сбоях, когда причиной вполне может оказаться глючная оперативная память или ошибки накопителей.

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

Разновидности ошибок

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

Баг в программе

Кодеры делят ошибки по сложности:

  1. Борбаг (Bohr Bug) – «стабильная» ошибка, легко выявляемая еще на этапе отладки или при бета-тестировании, когда речь еще не идет о выпуске стабильной версии.
  2. Гейзенбаг (Heisenbug) – периодически проявляющиеся, иногда надолго исчезающие баги с меняющимися свойствами, включая зависимость от программной среды, «железа».
  3. Мандельбаг (Mandelbug) – ошибка с энтропийным поведением, почти с непредсказуемым результатом.
  4. Шрединбаг (Schroedinbug) – критические баги, чаще приводящие к появлению возможности взлома, хотя внешне никак себя не проявляют.

Последняя категория ошибок – одна из основных причин регулярного обновления операционных систем Windows. Вроде бы пользователя все устраивает, а разработчик раз за разом выпускает новые пакеты исправлений. Наиболее известный баг, попортивший нервы многим кодерам, это «ошибка 2000 года» (Y2K Error). Про нее успешно забыли, но уроки извлекли.

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

Поиск ошибок

Логические

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

Синтаксические

Ошибки синтаксиса существуют на уровне конкретного языка программирования: C, Java, Python, Perl и т.д. Что на одной платформе работает максимум с ворнингами, для другой будет серьезной проблемой. Такие баги легко исправить на этапе компиляции, потому что инструмент не позволит «пройти дальше» некорректного участка кода.

Компиляционные

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

Среды выполнения

Так называемые ошибки Run-Time. Проявляются в скомпилированных программах, при запуске. Например, из-за нехватки ресурсов на машине, в результате аварийной ситуации (поломка памяти, носителя, устройств ввода-вывода). Такое происходит, если разработчик не учел реальных условий работы; придется вернуться к стадии проработки логики.

Арифметические

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

Ресурсные

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

Взаимодействия

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

Что такое исключение

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

Исключения ошибок

Существуют программные и аппаратные исключения. Первые генерируются самой программой и ОС, под которой она запущена. К аппаратным относятся те, что создаются процессором. Например, деление на 0, переполнение буфера, обращение к невыделенной памяти. Исключениями кодеры охватывают наиболее серьезные, критические баги.

Как избежать ошибок?

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

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

Выводы

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

Привет всем зрителям и слушателям нашего портала Loftblog! Продолжим наш увлекательный разговор о программировании. Наверно, не существует на свете программиста, который мог бы сразу написать код без ошибок. Ошибки программирования будут изначально присутствовать во всех программах, написанных вами. И на сегодняшнем видеоуроке мы поговорим об этих ошибках, рассмотрим их разновидности и узнаем, как их уменьшить.

История появления слова «Баг»

Ошибки в программах называются багами, а их исправление – отладкой или дебаггингом. С английского языка слово «баг» переводится как жук. Впервые оно было использовано в 1946 году Грейс Хоппер, которая работала в Гарвардском университете с вычислительной машиной Harvard Mark II.

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

Виды ошибок программирования

В программах есть 3 вида багов:

  • 1. Синтаксические ошибки программирования;
  • 2. Ошибки выполнения;
  • 3. Логические ошибки программирования.

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

Синтаксические ошибки программирования

Синтаксическими ошибками являются опечатки в ключевых словах или неверные отступы (актуально для Python). Такие ошибки программирования обнаруживаются перед выполнением программы и их легко отладить. Посмотрите внимательно на код и найдите в нем синтаксические ошибки:

[php]

i = 5

wile i < 15:

print (i)

i = i + 2

[/php]

Ошибки выполнения

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

[php]

print (‘dima’ + 1)

print (10/0)

[/php]

Логические ошибки программирования

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

[php]

age = 7

if (age < 5 and age > 8)

print (‘free ticket!’)

[/php]

Как же писать код без ошибок?

1. Проверьте свой код на синтаксические ошибки программирования, все ли ключевые слова написаны правильно? Заключен ли текст в кавычки? Сделаны ли необходимые отступы?

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

Ошибки программирования будут существовать всегда. Только от вашего профессионализма будет зависеть, насколько быстро будете вы их находить и насколько верно исправлять. А профессионализм достигается только с практикой, с опытом. Поэтому, дорогие друзья, практикуйтесь как можно больше.

Если с вами когда-нибудь случались забавные истории по поиску багов в коде, напишите их в комментариях. Всем спасибо!

Приятного всем просмотра! Учитесь с удовольствием! Всегда ваш LoftBlog!

Типы ошибок в программировании

Основные виды ошибок программирования

Дата добавления: 2013-12-23 ; просмотров: 4576 ; Нарушение авторских прав

Стиль программирования, облегчающий отладку

ОТЛАДКА ПРОГРАММ В СРЕДЕ ТУРБО-ПАСКАЛЬ

Опция Environment (состояние среды)

Позволяет настроить среду Турбо-Паскаля для удобства работы конкретного пользователя. У этой опции есть свое дополнительное меню:

По мнению экспертов, около 90% рабочего времени программиста затрачивается на отладку программы, и только 10% — на ее разработку и написание, поэтому лучшим средством облегчить неизбежную отладку является профилактика ошибок еще на этапе разработки программы и написания ее текста. Правильно написанная программа содержит меньше ошибок и значительно облегчает их поиск.

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

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

2. Желательно разбивать текст программы на процедуры и функции. Не следует писать большие процедуры (содержащие более 20-30 строк). Если процедура получается большой, лучше разбить ее на несколько подпрограмм меньших размеров.

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

4. Следует стремиться к простоте алгоритма программы. Главное- чтобы программа начала работать правильно, а уже потом можно украсить ее эффектными средствами ввода-вывода, добиться скорости работы и более рационального использования памяти.

5. Текст программы должен быть «рыхлым», не следует писать операторы компактно, вплотную друг к другу. Желательно, чтобы на строке было не более одного оператора. Поскольку отладчик Турбо-Паскаля работает построчно, такой подход облегчит локализацию ошибки. На одной строке в виде списка можно писать лишь те операторы, которые надежны в отношении отладки и в проверке не нуждаются: A:=10; B:=3.14; Str:=’Строка символов’; C:=True;.

6. Выполняемые в программе действия надо как можно чаще сопровождать комментариями < >, (* *). Известные программисты Керниган и Плоджер в своей книге «Элементы стиля программирования» рекомендуют, чтобы комментарием сопровождался каждый оператор программы.

В ходе отладки программы приходится сталкиваться с тремя видами ошибок:

1. Синтаксические ошибки — текст программы не отвечает требованиям языка Паскаль. Такие ошибки выявляются сразу при попытке компиляции и запуска программы.

2. Ошибки периода выполнения программы (RUNTIME ERROR) — ошибки, возникающие при выполнении программы, когда текст программы формально записан правильно, но при выполнении программы в результате неблагоприятного стечения обстоятельств возникает ошибочная ситуация и компьютер не может продолжить работу (например, в случае деления на ноль).

3. Ошибки в алгоритме (семантические ошибки) — программа работает без сбоев, но результаты получаются неверные.

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

Перечислим ошибки, наиболее часто допускаемые начинающими программистами, и разъясним сообщения Турбо-Паскаля, выдаваемые им при выявлении этих ошибок в процессе компиляции текста программы:

1. Identifier expected — в этом месте программы должен находиться идентификатор (имя программного объекта). Возможно, программист пытался использовать в качестве идентификатора слово, зарезервированное Паскалем для других целей.

2. Unknown identifier — идентификатор (имя переменной, константы, процедуры и т.п.) не был упомянут в разделе описаний программы.

3. Duplicate identifier — попытка дважды описать один и тот же идентификатор.

4. Syntax error — синтаксическая ошибка, например строка символов не была заключена в кавычки.

5. Line too long — компилятор не может обрабатывать текст программы со строками длиннее 126 символов. Скорее всего, программист забыл поставить апостроф, закрывающий текстовую строку, записываемую в переменную или выводимую на экран.

6. Type identifier expected — не указан тип идентификатора.

7. Variable identifier expected — на этом месте в программе должна стоять переменная.

8. Error in type — объявление типа данных не может начинаться с этого символа.

9. Type mismatch — а)тип переменной, стоящей слева от знака присваивания, отличается от значения выражения, стоящего справа; б)при обращении к процедуре типы формального и фактического параметров не совпадают; в)переменная данного типа не может служить индексом массива.

10. Begin expected — нужен begin.

11. End expected — нужен end.

12. Integer expression expected — требуется выражение типа integer.

13. Boolean expression expected — требуется выражение типа boolean.

14. Do expected — пропущено слово «Do».

15. Of expected — пропущено слово «of».

16. Then expected — пропущено слово «then».

17. To expected — пропущено слово «to».

18. String variable expected — требуется строковая переменная.

19. Error in expression — данный символ не может участвовать в выражении таким образом.

20. Division by zero — деление на ноль.

21. Constant and case types do not math — тип меток и тип селектора в операторе CASE не соответствуют друг другу.

22. Label not within current block — оператор GOTO не может ссылаться на метку, находящуюся вне текущего модуля.

23. Label already defined — такая метка уже есть.

24. Floating point overflow operation — величина вещественного числа вышла за пределы диапазона, допускаемого Паскалем (обычно при делении на ноль).

25. Invalid procedure or function reference — неправильный вызов подпрограммы.

5 видов ошибок, с которыми сталкивается каждый программист во время кодирования

Как программист, вы должны быть готовы исправлять ошибки в своем коде. Во время кодирования программы мы часто допускаем некоторые ошибки. Сделать сложную программу без помарки просто невозможно. Для многих программистов написание кода является легкой частью создания приложения. Трудным шагом является отладка (поиск ошибок и их исправление). И порой случается такое, что при устранении одной проблемы появляется 10 других. Какие виды ошибок в программировании существуют.

Виды ошибок в программировании:

Может возникнуть огромное число ошибок при компиляции.

Компиляция в программировании — трансляция программы, составленной на исходном языке высокого уровня, в эквивалентную программу на низкоуровневом языке, близком машинному коду (абсолютный код, объектный модуль, иногда на язык ассемблера).

Крошечные ошибки

Эти типы ошибок могут быть миниатюрными, но справиться с ними непросто. Вы получите ошибки компилятора, а затем потратите часы или даже дни, пытаясь выяснить, где вы ошиблись. Такие ошибки включают в себя забывание этой маленькой точки с запятой или скобки. В языке программирования, таком как Python, вы можете столкнуться с проблемой, когда отступ выполняется неправильно. Крошечные ошибки могут быть обнаружены при использовании правильных IDE. Маленькие ошибки являются наиболее раздражающими из всех, потому что вы знаете, что их легко исправить, но вы потратили так много времени, пытаясь найти их.

Несуществующие ошибки

Этот тип ошибок даже не существует. Тем не менее, вы получаете ошибки компилятора. Это может произойти, если вы используете неправильный или неисправный компилятор. Вы можете начать видеть ошибки, когда их нет. Старые компиляторы могут не поддерживать недавние изменения. Вы также должны обновлять свой компилятор как можно чаще. Маленький совет: выбирайте компилятор с большей осторожностью, чем выбираете спутника жизни. Иногда ваш код может работать отлично, но вы можете получить ошибки после обновления компилятора. Это означает, что компилятор просто показал вам существующие ошибки, которые не могли быть обнаружены ранее.

Большие ошибки

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

Скрытые ошибки

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

Сюрприз ошибок

Сюрприз ошибки появляются из ниоткуда. Ваш код может работать без нареканий сегодня. Так или иначе, это не будет хорошо работать на следующий день. Это заставит вас задуматься, не испортил ли кто-нибудь ваш код, пока вы спали.

Заключение

С большим временем написания кода вы становитесь лучше при отладке. Некоторые ошибки могут быть исправлены за 5 секунд. Другие могут быть исправлены в течение 5 дней. И есть некоторые ошибки, которые вы, возможно, никогда не исправите, к сожалению. Напомним, что вы можете создать 5 ошибок, пытаясь исправить две. Если ваш код работает, вы, вероятно, не должны его трогать.

И главное! Если у вас возникла ошибка, обращайтесь в поисковику. Думаю, что ранее с такой проблемой сталкивались и другие.

Логические ошибки: основные беды начинающих программистов

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

Второй тип – системные или логические ошибки – намного сложнее выявить. Компилятор их не замечает. Программа полностью работоспособна. Но в некоторых случаях она начинает выдавать результаты, отличные от ожидаемых.

Выявить этот вид багов удается только на этапе тестирования. И хорошо, если ошибку удается исправить локальной «заплаткой». Нередко приходится менять практически весь алгоритм. А это – дополнительные затраты времени, сил, а в коммерческих проектах – финансовые, а иногда и репутационные потери.

Застраховаться полностью от логических ошибок невозможно. Но вполне реально изучить самые распространенные типы таких багов и проверять на них программу на самых ранних этапах.

Алгоритм – основа всех основ

Написание алгоритма – это самый первый этап разработки, когда идеи только обретают форму еще без привязки к языку программирования. Нередко начинающие программисты относятся к созданию алгоритма «спустя рукава» — делают только «общие наброски» или вообще приступают к кодингу сразу без предварительной проработки логики «на бумаге».

Такой подход возможен при решении учебных задач на 10-15 строк кода. Но при работе над серьезным программным продуктом пренебрежение алгоритмом – почти гарантированный путь к логическим ошибкам и катастрофическим результатам.

Как работать с алгоритмом:

  • Начинайте с малого . Запишите алгоритм упрощенно, в виде «черных ящиков» (логических блоков без подробностей их работы). Это поможет оценить работоспособность идеи в целом.
  • Двигайтесь сверху вниз . Сначала – общая идея «в целом», далее – детализация основных функций и так далее. Не бойтесь ставить «заглушки» и прорабатывать мелкие детали в последнюю очередь. Двигаться сверху вниз проще и с точки зрения логики, и психологически.
  • Пишите команды «от имени компьютера». Помните, что вы имеете дело не с человеком, а с компьютером, который буквально выполняет команды и после каждого шага ждет ответа на вопрос «что делать». Например, логический блок «сохранение документа» будет понятен вам, но не компьютеру. Он вполне подойдет на этапе крупных блоков в качестве заглушки. Но далее придется проработать все действия пошагово с учетом выбранного языка программирования.
  • Делите код на отдельные модули (блоки) , которые можно будет запускать отдельно друг от друга. Это сильно облегчит как алгоритмизацию, так и процесс отладки.
  • Читайте алгоритм «как будто компьютер» . Проверяйте себя на каждом этапе. Главное правило – одинаковые данные всегда должны вести к одинаковым результатам.

Итак, алгоритм написан и проверен со всех сторон. Выбран язык программирования. Начинается процесс кодинга. Давайте разбираться, на что обращать особое внимание.

«Не туда положил»: о типах данных

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

  1. При статической типизации в таких языках, как С++, Java или С# неверно определен тип переменной. Большинство подобных ошибок выявляет компилятор. Но здесь есть свои «лазейки» для багов. Например, в С# вполне возможно «положить» вещественное значение в целочисленную переменную. И оно просто округлится до целого. Т.е. вместо 1,3 у вас будет храниться значение 1. Само собой, все дальнейшие вычисления будут содержать ошибку.
  2. При динамической типизации (JavaScript, Python, PHP) неявное приведение типов – самое обычное дело. А потому здесь даже компилятор промолчит в случае ошибки. Например, вы планируете получить целочисленное значение, для чего отправляете результаты вычислений в переменную типа int. Но программа видит «знаки после запятой», и переменная без вашего участия меняет тип на float.

Самый известный пример подобной ошибки – деление двух целых чисел с остатком.

Как вы думаете, какое число будет выведено на экран после выполнения последней строки? По идее, это должно быть 3,125. Но, например, в C# вы увидите целую цифру «3». Причем, тип переменной С будет float, как вы и заказывали.

Здесь проблема в другом: компилятор сначала проводит целочисленное деление, так как определяет переменные A и B как относящиеся к типу int. И полученный результат отправляет в переменную C (тип float). Целое значение (32 разряда) прекрасно помещается в 64-разрядный float, отведенный под хранение результата. Компилятор не видит ошибки. А у вас в программе появляются неточные вычисления, которые могут повлечь за собой большие проблемы.

Аналогичным образом компилятор округлит значение до целого и в Python 2. А уже в Python 3 алгоритм преобразования типов сработает иначе: сначала определится тип переменной, куда отправляется значение, а потом будет проводиться деление. После компиляции кода в Python 3 вы получите c=3,125.

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

Высвобождение ресурсов: до 100% загрузки процессора

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

Например, в Java этот процесс работает так:

  • Виртуальная машина проводит поиск ненужных объектов;
  • Составляет из них очередь на удаление;
  • По мере продвижения очереди очищает ячейки памяти.

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

Как итог, программа «загрязняет» память служебной информацией. Кроме того, формируется уязвимость: в этой «свалке данных» могут оказаться логины с паролями и другие личные данные.

Намного надежнее своевременно применять функции типа try-with-resources и try-finally. И все ресурсы очищать в том коде, где вы их получили.

И еще: не забывайте закрывать сессии и файлы сразу после того, как они перестают быть нужны. Это должно быть также естественно, как закрыть скобку в коде.

Конфликт потоков: кто первый успеет?

Если программа работает с несколькими потоками одновременно, необходимо исключить ситуацию конфликта потоков. Так бывает, когда процессы наперегонки пытаются работать с общими ресурсами, в итоге нарушают целостность данных и последовательность действий.

Например: первый поток в результате вычислений получает значение 1, отправляет его в переменную. В это время второй поток перехватывает доступ и обнуляет эту переменную. А первый – сохраняет значение. В результате вы планировали запомнить значение 1. А у вас сразу после вычислений сохраняется 0. И далее копятся ошибка за ошибкой.

Чтобы избежать этой проблемы не забывайте при работе с разными потоками ставить блокировки, чтобы они не обращались одновременно к одним и тем же ресурсам. Можно применять и другие методы синхронизации – события, семафоры, критические секции.

Переменные: склонность к глобализации

Эта ошибка популярна у новичков – стремление объявить сразу все переменные и сделать их глобальными. В результате таких действий вы:

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

На глобальном уровне определяют только необходимый минимум – те самые глобальные переменные, с которыми работают практически все модули. Все остальные объявляйте в тех модулях, где они работают. И не забывайте об идентификаторах ограничения доступа: public, private и protected.

Переполнение буфера в С/С++: «танцы на граблях»

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

Но нередко для экономии ресурсов программисты используют C-библиотеки. В этом случае очень важно следить за буферизацией. Дело в том, что языки C/С++ очень уязвимы к переполнению буфера. Если он окажется меньше, чем нужно для работы, программа попытается использовать память за пределами выделенного участка. Результат – многочисленные, можно сказать, легендарные ошибки, когда в обрабатываемые данные попадает «неведомый мусор».

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

Изучите особенности работы с буфером и методы борьбы с его переполнением, чтобы не пополнить число «танцующих на граблях с 30-летней историей».

Отладка и поиск логических багов

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

  • Пользуйтесь возможностями отладчика вашей IDE . Ставьте контрольные точки, отражайте на консоли ход выполнения и значения переменных, переходите в «пошаговый режим» выполнения в наиболее «подозрительных» участках кода. Так вы быстрее сможете локализовать проблему.
  • Помните: компилятор может неправильно указывать строку с ошибкой . Если вам повезло, и компилятор помог вам выявить баг, не спешите радоваться. При «завершении с ошибкой» вы видите номер строки, в которой выполнение программы стало невозможным. Если проблема в простейшей опечатке (синтаксис), то строка с багом вам известна. В случае логических ошибок вероятнее всего, проблема появилась на более ранних этапах работы программы. А в указанной строке была попытка использовать ошибочные данные, что и привело к аварийному завершению.
  • Старый добрый листинг программы тоже может помочь . Если вы запутались и не знаете, что делать, распечатайте код и попробуйте его «выполнить» как будто вы – и есть компьютер. Шаг за шагом двигайте по командам. Переходите от блока к блоку так, как это делает программа. На каждом этапе вычисляйте и фиксируйте значения переменных (калькулятором пользоваться можно). И сверяйте результаты с ожидаемыми. Все в порядке? Двигайтесь дальше. Что-то не так? Ура! Вы локализовали баг. Можно возвращаться за компьютер и разбираться подробнее в этом фрагменте кода в отладчике.

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

15 наиболее распространенных ошибок в программировании

Независимо от нашего уровня мастерства, все мы когда-то были новичками. И все мы время от времени можем допустить глупую ошибку. Текущая статья подготовлена авторами Nettuts+, которые были любезны поделиться их списками ошибок и их решениями, в различных языках.

Избегая ошибок в программировании, приведенных ниже, вы сможете существенно повысить качество создаваемого кода!

Советы по JavaScript

1 – Ненужные манипуляции с DOM

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

Этот код модифицирует DOM 99 раз, и создает 99 ненужных объектов jQuery. Девяносто девять – сумасшедшая цифра! Более правильным решением было бы использовать фрагмент документа или создать строку, содержащую 100 элементов
, а затем вставить это дело в HTML. Таким образом мы обращаемся к DOM всего один раз. Вот пример:

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

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

2 – Нелогичные имена Переменных и Функций в JavaScript

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

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

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

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

Иногда можно называть функцию указывая тип возвращаемого результата. Например вы можете назвать функцию которая возвращает HTML именем getTweetHTML() . Вы также можете предварять название функции словом do , если функция только выполняет какие-либо инструкции и ничего не возвращает, например: doFetchTweets() .

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

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

3 – Использование hasOwnProperty() в циклах for. in

Массивы в языке JavaScript не являются ассоциативными; использование их в качестве таких осуждается комьюнити. Объекты, с другой стороны, могут рассматриваться как хэш-таблицы. Такой подход подталкивает к перебору свойств объектов используя циклы for. in , например так:

Однако, нюанс в том, что цикл for. in перебирает свойства прототипа. Это может вызвать некоторые проблемы если вам нужно отобразить только существующие свойства выбранного объекта.

Можно решить эту проблему используя метод hasOwnProperty() . Вот пример:

Такой вариант выдаст только значения свойств, находящихся непосредственно в someObject .

4 – Сравнение булевых переменных

Сравнение булевых переменных это пустая трата вычислительного времени. Рассмотрим это на следующем примере:

Заметим что: foo == true . Сравнение foo и true не нужно, поскольку foo и без того булево значение (является ложью или истиной). Вместо сравнения foo , просто используем его значение, например:

Для значения false , используем логический оператор NOT (НЕ), как показано ниже:

5 – Назначение событий

События сложные объекты в JavaScript. Прошли времена встроенных событий вроде onlick (за редким исключением). Вместо этого используется делегация событий.

Представьте, что у вас есть “сетка” изображений, которые нужно отображать в лайтбоксе. Вот чего не надо делать. Заметим: мы здесь используем jQuery, надеясь что используете похожую библитеку. Впрочем, эти принципы можно использовать и в чистом JavaScript.

Этот код устанавливает вызов лайтбокса всем ссылкам, для просмотра полного размера изображения. Вместо использования всех ссылок, ограничимся элементом #grid-container .

В приведенном коде, и this и event.target ссылаются на элемент “ссылка”. Данная техника может применяться с любым родительским элементом. Только убедитесь, что правильно выбрали целевой элемент.

6 – Тернарная избыточность

Чрезмерное использование оператора тернаторсти обычное дело как в JavaScript, так и в PHP.

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

Советы по PHP

7 – Используйте тернарный оператор при необходимости

Оператор ветвления if. else является основой большинства языков. Но когда нужно сделать что-то простое, например присвоить значение в зависимости от состояния переменной, это не то следовало бы использовать. Посмотрите на следующий код:

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

Это четко, кратко, и обеспечивает всю необходимую функциональность.

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

8 – Используйте защищенные классы

Оператор if в основном используется для контроля пути выполнения функции или метода. Распространенное явление, когда выполняется большой фрагмент кода в случае когда условие true , и простой возврат значения в операторе else . Например:

Однако, такое решение может сильно усложнить читаемость вашего кода. Вы можете улучшить этот код всего лишь инвертировав условие. Вот улучшенная версия:

Легче читается, не правда ли? Это простое изменение, дающее резкое различие в читабельности кода.

9 – Поддерживайте понятность методов

Это одна из наиболее распространенных ошибок у новичков.

Метод объекта, это единица работы, и ограничение размера методов улучшает поддержку кода у улучшает его читаемость и восприятие. Рассмотрим это на следующем ужасном примере:

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

И вуаля наш код стал чище и легче для отладки!

10 – Избегайте большой вложенности

Слишком большее количество уровней вложенности делает ваш код сложным для чтения и обслуживания. Посмотрите на следующий код:

Используя ранее озвученный совет, “развернем” некоторые условия.

Этот код определенно лучше для восприятия, а делает тоже что и пример выше.

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

В таком случае извлеките вложенные методы, и объявите их собственные методы:

11 – Избегайте магических чисел и строк

Магические числа и строки это зло. Определяйте переменные или константы со значениями, которые вам хочется применить в коде.

Вместо подобной конструкции:

Укажите, что эти числа и строки значат, и присвойте их переменным с осмысленным именем, например так:

Кто-то может сказать что мы создаем ненужные переменные, но производительность пострадает незначительно. Читаемость кода всегда в приоритете. Запомните: не зацикливайтесь на производительности пока не сможете сказать зачем она вам нужна.

12 – Не борщите с переменными

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

Переменная $result здесь совсем ни к чему. Что и показывает следующий фрагмент:

Разница не велика, но мы смолги улучшить этот простой пример. Мы обошлись переменной $query обращающейся к базе данных, в то время как $result использовалась больше для логики.

Общие рекомендации про программированию

13 – Разумно называйте свои переменные

Дни когда переменные назывались x , y , z прошли (только если дело не касается систем координат конечно). Переменные представляют важную часть логики программы. Не хотите печатать длинное имя? Раздобудьте IDE по-лучше. Современные IDE имеют функцию автозавершения длинных имен.

Если вы будете постоянно писать код в течении шести месяцев. Вспомните ли вы что скрывает в себе переменная $sut , которую вы когда-то написали? Скорее всего нет: будьте описательны. Все короткое придает коду запашок.

14 – Методы описывающие действие

Ошибки случаются; важно учиться на них.

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

Кроме того, не допускайте применения любого другого языка, отличного от Английского. Порой раздражает видеть функции с именами вроде 做些什麼() или четоделать() в вашем проекте. Это может сделать непонятным для других программистов назначение ваших методов Сегодня Английский это стандарт де-факто в программировании. Используйте только его, особенно если вы работает в команде.

15 – Рекомендации по структуре

И наконец что касается структуры кода. Читаемость и понятность кода также важна как и все о чем мы сегодня говорили. Две рекомендации:

  • Используйте отступы в 2 или 4 пробела. Немного больше, например 8 пробелов, это слишком много и сделает ваш код сложным для чтения.
  • Установите разумную ширину и придерживайтесь ее. 40 символов в строке? Нет, мы уже не в 70х; установите ширину в 120 знаков, поставьте отметку на экране, и заставьте себя или вашу IDE соблюдать этот лимит. 120 знаков прекрасная ширина, не заставляющая скроллить.

Заключение.

“Я никогда не делал глупых ошибок программирования”

Ошибки случаются; важно учиться на них. Мы в Nettuts+ делали, и будем продолжать делать ошибки. Надеемся что вы научитесь на наших ошибках и постараетесь избегать их в будущем. Хотя, честно говоря, лучший способ научиться, это допустить собственные ошибки!

Обработка ошибок в программах на Си

Хотя C не обеспечивает прямой поддержки обработки ошибок (или обработки исключений), существуют способы, с помощью которых обработка ошибок может быть осуществлена в C. Программист должен в первую очередь предотвращать ошибки и тестировать возвращаемые значения из функций.
Многие вызовы функций C возвращают -1 или NULL в случае ошибки, поэтому быстрое тестирование этих возвращаемых значений легко выполнить, например, с помощью оператора if. Например, в Socket Programming возвращаемое значение функций, таких как socket (), listen () и т. Д., Проверяется на наличие ошибки или нет.

Пример: обработка ошибок в программировании сокетов

Различные методы обработки ошибок в C

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

// Реализация C, чтобы увидеть, как значение errno
// установить в случае любой ошибки в C
#include
#include

// Если файл открыт, который не существует,

// тогда это будет ошибка и соответствующая

// значение errno будет установлено

// открываем файл который

fp = fopen ( «GeeksForGeeks.txt» , «r» );

printf ( » Value of errno: %dn » , errno );

Примечание. Здесь значение errno установлено в 2, что означает — Нет такого файла или каталога. В онлайн-среде IDE может выдаваться ошибка № 13, в которой говорится, что разрешение отклонено.

  • perror () и strerror (): приведенное выше значение errno указывает типы возникших ошибок.
    Если требуется показать описание ошибки, то есть две функции, которые можно использовать для отображения текстового сообщения, связанного с errorno. Функции:
    • perror: отображает строку, которую вы передаете ей, затем двоеточие, пробел, а затем текстовое представление текущего значения errno.
      Синтаксис:
    • strerror (): возвращает указатель на текстовое представление текущего значения errno.
      Синтаксис:

    // Реализация C, чтобы увидеть, как perror () и strerror ()
    // функции используются для печати сообщений об ошибках.
    #include
    #include
    #include

    // Если файл открыт, который не существует,

    // тогда это будет ошибка и соответствующая

    // значение errno будет установлено

    fp = fopen ( » GeeksForGeeks.txt » , «r» );

    // открываем файл который

    printf ( «Value of errno: %dn » , errno );

    printf ( «The error message is : %sn» ,

    perror ( «Message from perror» );

    Выход:
    На личном рабочем столе:

    В онлайн IDE:

    Примечание . Функция perror () отображает переданную ей строку, за которой следует двоеточие и текстовое сообщение с текущим значением errno.

    Состояние выхода: Стандарт C определяет две константы: EXIT_SUCCESS и EXIT_FAILURE, которые могут быть переданы в exit () для указания успешного или неудачного завершения, соответственно. Это макросы, определенные в stdlib.h.

    // C реализация, которая показывает
    // использование EXIT_SUCCESS и EXIT_FAILURE.
    #include
    #include
    #include
    #include

  • Существует две фундаментальные стратегии: обработка исправимых ошибок (исключения, коды возврата по ошибке, функции-обработчики) и неисправимых (assert(), abort()). В каких случаях какую стратегию лучше использовать?

    Виды ошибок

    Ошибки возникают по разным причинам: пользователь ввёл странные данные, ОС не может дать вам обработчика файла или код разыменовывает (dereferences) nullptr. Каждая из описанных ошибок требует к себе отдельного подхода. По причинам ошибки делятся на три основные категории:

    • Пользовательские ошибки: здесь под пользователем подразумевается человек, сидящий перед компьютером и действительно «использующий» программу, а не какой-то программист, дёргающий ваш API. Такие ошибки возникают тогда, когда пользователь делает что-то неправильно.
    • Системные ошибки появляются, когда ОС не может выполнить ваш запрос. Иными словами, причина системных ошибок — сбой вызова системного API. Некоторые возникают потому, что программист передал системному вызову плохие параметры, так что это скорее программистская ошибка, а не системная.
    • Программистские ошибки случаются, когда программист не учитывает предварительные условия API или языка программирования. Если API требует, чтобы вы не вызывали foo() с 0 в качестве первого параметра, а вы это сделали, — виноват программист. Если пользователь ввёл 0, который был передан foo(), а программист не написал проверку вводимых данных, то это опять же его вина.

    Каждая из описанных категорий ошибок требует особого подхода к их обработке.

    Пользовательские ошибки

    Сделаю очень громкое заявление: такие ошибки — на самом деле не ошибки.

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

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

    Конечно, такое не всегда возможно. Иногда проверять вводимые данные слишком дорого, иногда это не позволяет сделать архитектура кода или разделение ответственности. Но в таких случаях ошибки должны обрабатываться однозначно как исправимые. Иначе, допустим, ваша офисная программа будет падать из-за того, что вы нажали backspace в пустом документе, или ваша игра станет вылетать при попытке выстрелить из разряженного оружия.

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

    Системные ошибки

    Обычно системные ошибки нельзя предсказать. Более того, они недетерминистские и могут возникать в программах, которые до этого работали без нареканий. В отличие от пользовательских ошибок, зависящих исключительно от вводимых данных, системные ошибки — настоящие ошибки.

    Но как их обрабатывать, как исправимые или неисправимые?

    Это зависит от обстоятельств.

    Многие считают, что ошибка нехватки памяти — неисправимая. Зачастую не хватает памяти даже для обработки этой ошибки! И тогда приходится просто сразу же прерывать выполнение.

    Но падение программы из-за того, что ОС не может выделить сокет, — это не слишком дружелюбное поведение. Так что лучше бросить исключение и позволить catch аккуратно закрыть программу.

    Но бросание исключения — не всегда правильный выбор.

    Кто-то даже скажет, что он всегда неправильный.

    Если вы хотите повторить операцию после её сбоя, то обёртывание функции в try-catch в цикле — медленное решение. Правильный выбор — возврат кода ошибки и цикличное исполнение, пока не будет возвращено правильное значение.

    Если вы создаёте вызов API только для себя, то просто выберите подходящий для своей ситуации путь и следуйте ему. Но если вы пишете библиотеку, то не знаете, чего хотят пользователи. Дальше мы разберём подходящую стратегию для этого случая. Для потенциально неисправимых ошибок подойдёт «обработчик ошибок», а при других ошибках необходимо предоставить два варианта развития событий.

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

    Программистские ошибки

    Это худший вид ошибок. Для их обработки я стараюсь сделать так, чтобы мои ошибки были связаны только с вызовами функций, то есть с плохими параметрами. Прочие типы программистских ошибок могут быть пойманы только в runtime, с помощью отладочных макросов (assertion macros), раскиданных по коду.

    При работе с плохими параметрами есть две стратегии: дать им определённое или неопределённое поведение.

    Если исходное требование для функции — запрет на передачу ей плохих параметров, то, если их передать, это считается неопределённым поведением и должно проверяться не самой функцией, а оператором вызова (caller). Функция должна делать только отладочное подтверждение (debug assertion).

    С другой стороны, если отсутствие плохих параметров не является частью исходных требований, а документация определяет, что функция будет бросать bad_parameter_exception при передаче ей плохого параметра, то передача — это хорошо определённое поведение (бросание исключения или любая другая стратегия обработки исправимых ошибок), и функция всегда должна это проверять.

    В качестве примера рассмотрим получающие функции (accessor functions) std::vector<T>: в спецификации на operator[] говорится, что индекс должен быть в пределах валидного диапазона, при этом at() сообщает нам, что функция кинет исключение, если индекс не попадает в диапазон. Более того, большинство реализаций стандартных библиотек обеспечивают режим отладки, в котором проверяется индекс operator[], но технически это неопределённое поведение, оно не обязано проверяться.

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

    Когда нужно проверять только с помощью отладочных подтверждений, а когда — постоянно?

    К сожалению, однозначного рецепта нет, решение зависит от конкретной ситуации. У меня есть лишь одно проверенное правило, которому я следую при разработке API. Оно основано на наблюдении, что проверять исходные условия должен вызывающий, а не вызываемый. А значит, условие должно быть «проверяемым» для вызывающего. Также условие «проверяемое», если можно легко выполнить операцию, при которой значение параметра всегда будет правильным. Если для параметра это возможно, то это получается исходное условие, а значит, проверяется только посредством отладочного подтверждения (а если слишком дорого, то вообще не проверяется).

    Но конечное решение зависит от многих других факторов, так что очень трудно дать какой-то общий совет. По умолчанию я стараюсь свести к неопределённому поведению и использованию только подтверждений. Иногда бывает целесообразно обеспечить оба варианта, как это делает стандартная библиотека с operator[] и at().

    Хотя в ряде случаев это может быть ошибкой.

    Об иерархии std::exception

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

    Я предлагаю наследовать только от одного из этих четырёх классов:

    • std::bad_alloc: для сбоев выделения памяти.
    • std::runtime_error: для общих runtime-ошибок.
    • std::system_error (производное от std::runtime_error): для системных ошибок с кодами ошибок.
    • std::logic_error: для программистских ошибок с определённым поведением.

    Обратите внимание, что в стандартной библиотеке разделяются логические (то есть программистские) и runtime-ошибки. Runtime-ошибки — более широкое определение, чем «системные». Оно описывает «ошибки, обнаруживаемые только при выполнении программы». Такая формулировка не слишком информативна. Лично я использую её для плохих параметров, которые не являются исключительно программистскими ошибками, а могут возникнуть и по вине пользователей. Но это можно определить лишь глубоко в стеке вызовов. Например, плохое форматирование комментариев в standardese приводит к исключению при парсинге, проистекающему из std::runtime_error. Позднее оно ловится на соответствующем уровне и фиксируется в логе. Но я не стал бы использовать этот класс иначе, как и std::logic_error.

    Подведём итоги

    Есть два пути обработки ошибок:

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

    Подтверждения — это особый вид стратегии обработки неисправимых ошибок, только в режиме отладки.

    Есть три основных источника ошибок, каждый требует особого подхода:

    • Пользовательские ошибки не должны обрабатываться как ошибки на верхних уровнях программы. Всё, что вводит пользователь, должно проверяться соответствующим образом. Это может обрабатываться как ошибки только на нижних уровнях, которые не взаимодействуют с пользователями напрямую. Применяется стратегия обработки исправимых ошибок.
    • Системные ошибки могут обрабатываться в рамках любой из двух стратегий, в зависимости от типа и тяжести. Библиотеки должны работать как можно гибче.
    • Программистские ошибки, то есть плохие параметры, могут быть запрещены исходными условиями. В этом случае функция должна использовать только проверку с помощью отладочных подтверждений. Если же речь идёт о полностью определённом поведении, то функции следует предписанным образом сообщать об ошибке. Я стараюсь по умолчанию следовать сценарию с неопределённым поведением и определяю для функции проверку параметров лишь тогда, когда это слишком трудно сделать на стороне вызывающего.

    Гибкие методики обработки ошибок в C++

    Иногда что-то не работает. Пользователи вводят данные в недопустимом формате, файл не обнаруживается, сетевое соединение сбоит, в системе кончается память. Всё это ошибки, и их надо обрабатывать.

    Это относительно легко сделать в высокоуровневых функциях. Вы точно знаете, почему что-то пошло не так, и можете обработать это соответствующим образом. Но в случае с низкоуровневыми функциями всё не так просто. Они не знают, что пошло не так, они знают лишь о самом факте сбоя и должны сообщить об этом тому, кто их вызвал.

    В C++ есть два основных подхода: коды возврата ошибок и исключения. Сегодня широко распространено использование исключений. Но некоторые не могут / думают, что не могут / не хотят их использовать — по разным причинам.

    Я не буду принимать чью-либо сторону. Вместо этого я опишу методики, которые удовлетворят сторонников обоих подходов. Особенно методики пригодятся разработчикам библиотек.

    Проблема

    Я работаю над проектом foonathan/memory. Это решение предоставляет различные классы выделения памяти (allocator classes), так что в качестве примера рассмотрим структуру функции выделения.

    Для простоты возьмём malloc(). Она возвращает указатель на выделяемую память. Если выделить память не получается, то возвращается nullptr, то есть NULL, то есть ошибочное значение.

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

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

    Это можно расценить как недостаток.

    Но в подобных ситуациях исключения имеют также очень большое преимущество: функция выделения памяти либо возвращает валидную память, либо вообще ничего не возвращает. Это функция «всё или ничего», возвращаемое значение всегда будет валидным. Это полезное следствие согласно принципу Скотта Майера «Make interfaces hard to use incorrectly and easy to use correctly».

    Учитывая вышесказанное, можно утверждать, что вам следует использовать исключения в качестве механизма обработки ошибок. Этого мнения придерживается большинство разработчиков на С++, включая и меня. Но проект, которым я занимаюсь, — это библиотека, предоставляющая средства выделения памяти, и предназначена она для приложений, работающих в реальном времени. Для большинства разработчиков подобных приложений (особенно для игроделов) само использование исключений — исключение.

    Каламбур детектед.

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

    Так что же делать?

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

    К счастью, я могу определить, что вы делаете, когда обнаруживаете ошибку нехватки памяти: чаще всего вы журналируете это событие и прерываете программу, поскольку она не может корректно работать без памяти. В таких ситуациях исключения — просто способ передачи контроля другой части кода, которая журналирует и прерывает программу. Но есть старый и эффективный способ передачи контроля: указатель функции (function pointer), то есть функция-обработчик (handler function).

    Если у вас включены исключения, то вы просто их бросаете. В противном случае вызываете функцию-обработчика и затем прерываете программу. Это предотвратит бесполезную работу функции-обработчика, та позволит программе продолжить выполняться в обычном режиме. Если не прервать, то произойдёт нарушение обязательного постусловия функции: всегда возвращать валидный указатель. Ведь на выполнении этого условия может быть построена работа другого кода, да и вообще это нормальное поведение.

    Я называю такой подход обработкой исключений и придерживаюсь его при работе с памятью.

    Решение 1: обработчик исключений

    Если вам нужно обработать ошибку в условиях, когда наиболее распространённым поведением будет «журналировать и прервать», то можно использовать обработчика исключений. Это такая функция-обработчик, которая вызывается вместо бросания объекта-исключения. Её довольно легко реализовать даже в уже существующем коде. Для этого нужно поместить управление обработкой в класс исключений и обернуть в макрос выражение throw.

    Сначала дополним класс и добавим функции для настройки и, возможно, запрашивания функции-обработчика. Я предлагаю делать это так же, как стандартная библиотека обрабатывает std::new_handler:

    class my_fatal_error
    {
    public:
        // тип обработчика, он должен брать те же параметры, что и конструктор,
        // чтобы у них была одинаковая информация
        using handler = void(*)( ... );
    
        // меняет функцию-обработчика
        handler set_handler(handler h);
    
        // возвращает текущего обработчика
        handler get_handler();
    
        ... // нормальное исключение
    };

    Поскольку это входит в область видимости класса исключений, вам не нужно именовать каким-то особым образом. Отлично, нам же легче.

    Если исключения включены, то для удаления обработчика можно использовать условное компилирование (conditional compilation). Если хотите, то также напишите обычный подмешанный класс (mixin class), дающий требуемую функциональность.

    Конструктор исключений элегантен: он вызывает текущую функцию-обработчика, передавая ей требуемые аргументы из своих параметров. А затем комбинирует с последующим макросом throw:

    If```cpp #if EXCEPTIONS #define THROW(Ex) throw (Ex) #else #define THROW(Ex) (Ex), std::abort() #endif

    > Такой макрос throw также предоставляется [foonathan/compatiblity](https://github.com/foonathan/compatibility).
    
    Можно использовать его и так:
    
    ```cpp
    THROW(my_fatal_error(...))

    Если у вас включена поддержка исключений, то будет создан и брошен объект-исключение, всё как обычно. Но если поддержка выключена, то объект-исключение всё равно будет создан, и — это важно — только после этого произойдёт вызов std::abort(). А поскольку конструктор вызывает функцию-обработчика, то он и работает, как требуется: вы получаете точку настройки для журналирования ошибки. Благодаря же вызову std::abort() после конструктора пользователь не может нарушить постусловие.

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

    Так что при этой методике вам ещё будет доступна определённая степень кастомизации, даже если вы отключите исключения. Конечно, замена неполноценная, мы только журналируем и прерываем работу программы, без дальнейшего продолжения. Но в ряде случаев, в том числе при исчерпании памяти, это вполне пригодное решение.

    А если я хочу продолжить работу после бросания исключения?

    Методика с обработчиком исключений не позволяет этого сделать в связи с постусловием кода. Как же тогда продолжить работу?

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

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

    Для примера снова возьмём функцию выделения памяти. В этом случае я использую такие функции:

    void* try_malloc(..., int &error_code) noexcept;
    
    void* malloc(...);

    При сбое выделения памяти первая версия возвращает nullptr и устанавливает error_code в коде ошибки. Вторая версия не возвращает nullptr, зато бросает исключение. Обратите внимание, что в рамках первой версии очень легко реализовать вторую:

    void* malloc(...)
    {
        auto error_code = 0;
        auto res = try_malloc(..., error_code);
        if (!res)
            throw malloc_error(error_code);
        return res;
    }

    Не делайте этого в обратной последовательности, иначе вам придётся ловить исключение, а это дорого. Также это не даст нам скомпилировать код без включённой поддержки исключений. Если сделаете, как показано, то можете просто стереть другую перегрузку (overload) с помощью условного компилирования.

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

    Решение 2: предоставить две перегрузки

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

    Если рассматриваемая функция не имеет возвращаемого значения, то можете её использовать для кода ошибки. В противном случае вам придётся возвращать недопустимое значение для сигнализирования об ошибке — как nullptr в вышеприведённом примере, — а также установить выходной параметр для кода ошибки, если хотите предоставить вызывающему дополнительную информацию.

    Пожалуйста, не используйте глобальную переменную errno или что-то типа GetLastError()!

    Если возвращаемое значение не содержит недопустимое значение для обозначения сбоя, то по мере возможности используйте std::optional или что-то похожее.

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

    std::system_error

    Подобная система идеально подходит для работы с кодами ошибок в С++ 11.

    Она возвращает непортируемый (non-portable) код ошибки std::error_code, то есть возвращаемый функцией операционной системы. С помощью сложной системы библиотечных средств и категорий ошибок вы можете добавить собственные коды ошибок, или портируемые std::error_condition. Для начала почитайте об этом здесь. Если нужно, то можете использовать в функции кода ошибки std::error_code. А для функции исключения есть подходящий класс исключения: std::system_error. Он берёт std::error_code и применяется для передачи этих ошибок в виде исключений.

    Эту или подобную систему должны использовать все низкоуровневые функции, являющиеся закрытыми обёртками ОС-функций. Это хорошая — хотя и сложная — альтернатива службе кодов ошибок, предоставляемой операционной системой.

    Да, и мне ещё нужно добавить подобное в функции виртуальной памяти. На сегодняшний день они не предоставляют коды ошибок.

    std::expected

    Выше упоминалось о проблеме, когда у вас нет возвращаемого значения, содержащего недопустимое значение, которое можно использовать для сигнализирования об ошибке. Более того, выходной параметр — не лучший способ получения кода ошибки.

    А глобальные переменные вообще не вариант!

    В № 4109 предложено решение: std::expected. Это шаблон класса, который также хранит возвращаемое значение или код ошибки. В вышеприведённом примере он мог бы использоваться так:

    std::expected<void*, std::error_code> try_malloc(...);

    В случае успеха std::expected будет хранить не-null указатель памяти, а при сбое — std::error_code. Сейчас эта методика работает при любых возвращаемых значениях. Комбинация std::expected и функции исключения определённо допускает любые варианты использования.

    Заключение

    Если вы создаёте библиотеки, то иногда приходится обеспечивать максимальную гибкость использования. Под этим подразумевается и разнообразие средств обработки ошибок: иногда требуются коды возврата, иногда — исключения.

    Одна из возможных стратегий — улаживание этих противоречий с помощью обработчика исключений. Просто удостоверьтесь, что когда нужно, то вызывается callback, а не бросается исключение. Это замена для критических ошибок, которая в любом случае будет журналироваться перед прерыванием работы программы. Как таковой этот способ не универсален, вы не можете переключаться в одной программе между двумя версиями. Это лишь обходное решение при отключённой поддержке исключений.

    Более гибкий подход — просто предоставить две перегрузки, одну с исключениями, а вторую без. Это даст пользователям максимальную свободу, они смогут выбирать ту версию, что лучше подходит в их ситуации. Недостаток этого подхода: вам придётся больше потрудиться при создании библиотеки.

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

    Определение

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

    Баги обнаруживаются чаще всего в момент отладки или бета-тестирования. Реже – после итогового релиза готовой программы. Вот несколько вариантов багов:

    1. Появляется сообщение об ошибке, но приложение продолжает функционировать.
    2. ПО вылетает или зависает. Никаких предупреждений или предпосылок этому не было. Процедура осуществляется неожиданно для пользователя. Возможен вариант, при котором контент перезапускается самостоятельно и непредсказуемо.
    3. Одно из событий, описанных ранее, сопровождается отправкой отчетов разработчикам.

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

    История происхождения термина

    Баг – слово, которое используется разработчиками в качестве сленга. Оно произошло от слова «bug» – «жук». Точно неизвестно, откуда в программировании и IT возник соответствующий термин. Существуют две теории:

    1. 9 сентября 1945 года ученые из Гарварда тестировали очередную вычислительную машину. Она называлась Mark II Aiken Relay Calculator. Устройство начало работать с ошибками. Когда его разобрали, то ученые заметили мотылька, застрявшего между реле. Тогда некая Грейс Хоппер назвала произошедший сбой упомянутым термином.
    2. Слово «баг» появилось задолго до появления Mark II. Термин использовался Томасом Эдисоном и указывал на мелкие недочеты и трудности. Во время Второй Мировой войны «bugs» называли проблемы с радарной электроникой.

    Второй вариант кажется более реалистичным. Это факт, который подтвержден документально. Со временем научились различать различные типы багов в IT. Далее они будут рассмотрены более подробно.

    Как классифицируют

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

    1. Серьезные неполадки. Это нарушения работоспособности приложения, которые могут приводить к непредвиденным крупным изменениям.
    2. Незначительные ошибки в программах. Чаще всего не оказывают серьезного воздействия на функциональность ПО.
    3. Showstopper. Критические проблемы в приложении или аппаратном обеспечении. Приводят к выходу программы из строя почти всегда. Для примера можно взять любое клиент-серверное приложение, в котором не получается авторизоваться через логин и пароль.

    Последний вариант требует особого внимания со стороны программистов. Их стараются обнаружить и устранить в первую очередь. Критические ошибки могут отложить релиз исходной программы на неопределенный срок.

    Также существуют различные виды сбоев в плане частоты проявления: постоянные и «разовые». Вторые встречаются редко, чаще – при определенных настройках и действиях со стороны пользователя. Первые появляются независимо от используемой платформы и выполненных клиентом манипуляций.

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

    Виды

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

    Разработчики выделяют следующие типы ошибок по уровню сложности:

    1. «Борбаг» – «стабильная» неполадка. Она легко обнаруживается на этапе разработки и компилирования. Иногда – во время тестирования наработкой исходной программы.
    2. «Гейзенбаг» – баги с поддержкой изменения свойств, включая зависимость от среды, в которой было запущено приложение. Сюда относят периодические неполадки в программах. Они могут исчезать на некоторое время, но через какой-то промежуток вновь дают о себе знать.
    3. «Мандельбаг» – непредвиденные ошибки. Обладают энтропийным поведением. Предсказать, к чему они приведут, практически невозможно.
    4. «Шрединбаг» – критические неполадки. Приводят к тому, что злоумышленники могут взломать программу. Данный тип ошибок обнаружить достаточно трудно, потому что они никак себя не проявляют.

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

    Типы багов

    Ошибки в программах бывают:

    • логическими;
    • синтаксическими;
    • взаимодействия;
    • компиляционные;
    • ресурсные;
    • арифметические;
    • среды выполнения.

    Это – основная классификация сбоев в приложениях и операционных системах. Логические, синтаксические и «среды выполнения» встречаются в разработке чаще остальных. На них будет сделан основной акцент.

    Ошибки синтаксиса

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

    Синтаксические ошибки – ошибки синтаксиса, правил языка. Вот пример в Паскале:

    Код написан неверно. Согласно действующим синтаксическим нормам, в Pascal в первой строчке нужно в конце поставить точку с запятой.

    Логические

    Тут стоит выделить обычные и арифметические типы. Вторые возникают, когда программе при работе необходимо вычислить много переменных, но на каком-то этапе расчетов возникают неполадки или нечто непредвиденное. Пример – получение в результатах «бесконечности».

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

    Выше – пример логической ошибки в программе. Тут:

    1. Происходит сравнение значения i с 15.
    2. На экран выводится сообщение, если I = 15.
    3. В заданном цикле i не будет равно 15. Связано это с диапазоном значений – от 1 до 10.

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

    Время выполнения

    Run-time сбои – это ошибка времени выполнения программы. Встречается даже когда исходный код лишен логических и синтаксических ошибок. Связаны такие неполадки с ходом выполнения программного продукта. Пример – в процессе функционирования ПО был удален файл, считываемый программой. Если игнорировать подобные неполадки, можно столкнуться с аварийным завершением работы контента.

    Самый распространенный пример в данной категории – это неожиданное деление на ноль. Предложенный фрагмент кода с точки зрения синтаксиса и логики написан грамотно. Но, если клиент наберет 0, произойдет сбой системы.

    Компиляционный тип

    Встречается при разработке на языках высокого уровня. Во время преобразований в машинный тип «что-то идет не так». Причиной служат синтаксические ошибки или сбои непосредственно в компиляторе.

    Наличие подобных неполадок делает бета-тестирование невозможным. Компиляционные ошибки устраняются при разработке-отладке.

    Ресурсные

    Ресурсный тип ошибок – это сбои вроде «переполнение буфера» или «нехватка памяти». Тесно связаны с «железом» устройства. Могут быть вызваны действиями пользователя. Пример – запуск «свежих» игр на стареньких компьютерах.

    Исправить ситуацию помогают основательные работы над исходным кодом. А именно – полное переписывание программы или «проблемного» фрагмента.

    Взаимодействие

    Подразумевается взаимодействие с аппаратным или программным окружением. Пример – ошибка при использовании веб-протоколов. Это приведет к тому, что облачный сервис не будет нормально функционировать. При постоянном возникновении соответствующей неполадки остается один путь – полностью переписывать «проблемный» участок кода, ответственный за соответствующий баг.

    Исключения и как избежать багов

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

    Исключения бывают:

    1. Программными. Они генерируются приложением или ОС.
    2. Аппаратными. Создаются процессором. Пример – обращение к невыделенной памяти.

    Исключения нужны для охвата критических багов. Избежать неполадок помогут отладчики на этапе разработки. А еще – своевременное поэтапное тестирование программы.

    P. S. Большой выбор курсов по тестированию есть и в Otus. Присутствуют варианты как для продвинутых, так и для начинающих пользователей.

    Мы поможем в написании ваших работ!

    ЗНАЕТЕ ЛИ ВЫ?

    ОСКОЛЬСКИЙ ПОЛИТЕХНИЧЕСКИЙ КОЛЛЕДЖ

      Утверждены:
    решением Учёного совета
    СТИ НИТУ «МИСиС»
    от «22» июня 2020 г.
    протокол № 23

    ПМ.03 Ревьюирование программных продуктов

    УП 03. Учебная практика

    Методические указания

    для студентов очной формы обучения

    По выполнению практических работ

    (в редакции 2020 г.)

    Наименование специальности: 09.02.07 Информационные системы и программирование

    Год набора: 2018

    Квалификация выпускника: специалист по информационным системам

    Срок освоения: 3 года 10 месяцев

    Методические указания составлены в соответствии с рабочей программой ПМ.03 Ревьюирование программных продуктов специальности 09.02.07 Информационные системы и программирование

    Разработчик:

    Артюхина Д.Д. – преподаватель ОПК СТИ НИТУ «МИСиС»

    Рекомендованы:

    П(Ц)К специальностей 09.02.04, 09.02.07

    протокол № 09 от «20» мая 2020 г.

    Председатель П(Ц)К ____________ Назарова О.И.

    Согласованы:

    на заседании НМС ОПК

    протокол № 05 от «03» июня 2020 г.

    Председатель НМС ___________ Дерикот О.В.

    Содержание

    Введение. 4

    Практическая работа №1. 7

    Ревьюирование части информационной системы для определённого рабочего места 7

    Практическая работа №2. 11

    Тестирование информационной системы различными способами. 11

    Практическая работа №3. 14

    Описание выявленных ошибок и способов их устранения. Разработка инструкции по устранению выявленных ошибок информационной системы. 14

    Практическая работа №4. 17

    Оценка качества и надежности системы по результатам ее исследования. 17

    Практическая работа №5. 21

    Проведение работ по оптимизации программного кода. 21

    Практическая работа №6. 24

    Формирование отчетной документации по результатам работ. 24

    Список использованных источников. 28

    Введение

    ПМ.03 Ревьюирование программных продуктов является частью программы подготовки специалиста среднего звена в соответствии с ФГОС СПО по специальности 09.02.07 Информационные системы и программирование.

    Актуальность изучаемого профессионального модуля ПМ.03 Ревьюирование программных продуктов заключается в формировании у студентов общих и профессиональных компетенций, владение которыми позволят осуществлять исследование созданного программного кода с использованием специализированных программных средств.

    В результате освоения профессионального модуля обучающийся должен уметь:

    — работать с проектной документацией, разработанной с использованием графических языков спецификаций;

    — выполнять оптимизацию программного кода с использованием специализированных программных средств;

    — использовать методы и технологии тестирования и ревьюирования кода и проектной документации;

    — применять стандартные метрики по прогнозированию затрат, сроков и качества;

    — распознавать задачу и/или проблему в профессиональном и/или социальном контексте;

    — владеть актуальными методами работы в профессиональной и смежных сферах;

    — определять задачи для поиска информации; определять необходимые источники информации;

    — определять и выстраивать траектории профессионального развития и самообразования;

    — взаимодействовать с коллегами, руководством, клиентами в ходе профессиональной деятельности;

    — грамотно излагать свои мысли и оформлять документы по профессиональной тематике на государственном языке, проявлять толерантность в рабочем коллективе;

    — описывать значимость своей специальности;

    — определять направления ресурсосбережения в рамках профессиональной деятельности по специальности;

    — применять рациональные приемы двигательных функций в профессиональной деятельности;

    — применять средства информационных технологий для решения профессиональных задач;

    — строить простые высказывания о себе и о своей профессиональной деятельности.

    В результате освоения профессионального модуля обучающийся должен знать:

    — задачи планирования и контроля развития проекта;

    — принципы построения системы деятельностей программного проекта;

    — современные стандарты качества программного продукта и процессов его обеспечения;

    — основные источники информации и ресурсы для решения задач и проблем в профессиональном и/или социальном контексте;

    — алгоритмы выполнения работ в профессиональной и смежных областях; методы работы в профессиональной и смежных сферах;

    — номенклатура информационных источников, применяемых в профессиональной деятельности;

    — возможные траектории профессионального развития и самообразования;

    — основы проектной деятельности;

    — правила оформления документов и построения устных сообщений;

    — значимость профессиональной деятельности по специальности;

    — основные ресурсы, задействованные в профессиональной деятельности;

    — условия профессиональной деятельности и зоны риска физического здоровья для

    — специальности;

    — порядок применения программного обеспечения в профессиональной деятельности;

    — лексический минимум, относящийся к описанию предметов, средств и процессов профессиональной деятельности.

    Методические указания по выполнению практических работ по ПМ.03 Ревьюирование программных продуктов УП 03 Учебная практика предназначены для обучающихся ­­3 курса специальности 09.02.07 Информационные системы и программирование.

    Практические работы проводятся с целью получения практического опыта в области:

    — в измерении характеристик программного проекта;

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

    — оптимизации программного кода с использованием специализированных программных средств.

    Выполнение обучающимися практических работ направлено на формирование элементов общих компетенций:

    ОК 01.       Выбирать способы решения задач профессиональной деятельности, применительно к различным контекстам

    ОК 02.       Осуществлять поиск, анализ и интерпретацию информации, необходимой для выполнения задач профессиональной деятельности.

    ОК 03.       Планировать и реализовывать собственное профессиональное и личностное развитие.

    ОК 04.       Работать в коллективе и команде, эффективно взаимодействовать с коллегами, руководством, клиентами.

    ОК 05.       Осуществлять устную и письменную коммуникацию на государственном языке с учетом особенностей социального и культурного контекста.

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

    ОК 07.       Содействовать сохранению окружающей среды, ресурсосбережению, эффективно действовать в чрезвычайных ситуациях.

    ОК 08.       Использовать средства физической культуры для сохранения и укрепления здоровья в процессе профессиональной деятельности и поддержания необходимого уровня физической подготовленности

    ОК 09.       Использовать информационные технологии в профессиональной деятельности.

    ОК 10.       Пользоваться профессиональной документацией на государственном и иностранном языке

           Выполнение обучающимися практических работ направлено на формирование элементов профессиональных компетенций:

    ПК 3.1       Осуществлять ревьюирование программного кода в соответствии с технической документацией

    ПК 3.2       Выполнять измерение характеристик компонент программного продукта для определения соответствия заданным критериям

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

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

    Практическая работа №1

    Задание:

    1. Изучить теоретическую часть.

    2. Выполнить задание, следуя указаниям.

    3. Ответить на контрольные вопросы (в устной форме).

    4. Предъявить преподавателю результаты работы программы и исходные коды.

    5. Оформить отчет в соответствии с ходом работы (тема, цель, условие задачи, программный код, результаты тестирования программы, выводы).

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

    Не во всех случаях возможна разработка автоматических или хотя бы четко формализованных ручных тестов для проверки функциональности программной системы. В некоторых случаях выполнение программного кода, подвергаемого тестированию, невозможно в условиях, создаваемых тестовым окружением (например, во встроенных системах, если программный код предназначен для обработки исключительных ситуаций, создаваемых только при установке системы на реальное оборудование). В других случаях верифицируется не программный код, а проектная документация на систему, которую нельзя «выполнить» или создать для нее отдельные тестовые примеры. И в тех и в других случаях обычно прибегают к методу экспертных исследований программного кода или документации на корректность или непротиворечивость.

    Такие экспертные исследования обычно называют инспекциями или просмотрами. Существует два типа инспекций – неформальные и формальные.

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

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

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

    Рис.1. Место формальной инспекции в процессе разработки программных систем

    Выполнение работы:

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

    Добавление в ваш проект нового класса

    Класс, определенный пользователем, позволяет определить в программе ваши собственные объекты, которые имеют свойства, методы и события, точно так же, как объекты, создаваемые на формах Windows с помощью элементов управления из Области элементов. Чтобы добавить в ваш проект новый класс, щелкните в меню Проект (Project) на команде Добавить класс (AddClass), а затем определите этот класс с помощью кода программы и нескольких новых ключевых слов VisualBasic.

    Создание свойств

    1. Под объявлением переменных введите следующий оператор программы и нажмите клавишу (Enter):

    Этот оператор создает свойство вашего класса с именем FirstName, которое имеет тип String. Когда вы нажмете (Enter), VisualStudio немедленно создаст структуру кода для остальных элементов объявления свойства. Требуемыми элементами являются: блок Get, который определяет, что программисты увидят, когда будут проверять свойство FirstName, блок Set, который определяет, что произойдет, когда свойство FirstName будет установлено или изменено, и оператор EndProperty, который отмечает конец процедуры свойства.

    2. Заполните структуру процедуры свойства так, чтобы она выглядела, как показано ниже.

    Ключевое слово Return указывает, что при обращении к свойству FirstName будет возвращена строковая переменная Name1. При установке значения свойства блок Set присваивает переменной Name1 строковое значение. Обратите особое внимание на переменную Value, используемую в процедурах свойств для обозначения значения, которое присваивается свойству класса при его установке. Хотя этот синтаксис может выглядеть странно, просто поверьте мне — именно так создаются свойства в элементах управления, хотя более сложные свойства будут иметь здесь дополнительную программную логику, которая будет проверять значения и производить вычисления.

    3. Под оператором EndProperty введите для свойства LastName вашего класса вторую процедуру свойства. Она должна выглядеть так, как показано ниже.

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

    Создание метода

    · Под процедурой свойства LastName введите следующее определение функции:

    Чтобы создать метод класса, который выполняет некое действие, добавьте в ваш класс процедуру Sub. Хотя многие методы не требуют для выполнения своей работы аргументов, метод Age, определенный мной, требует для своих вычислений аргумент Birthday типа Date. Это метод использует для вычитания даты рождения нового сотрудника из текущей системной даты метод Subtract, и возвращает значение, выраженное в днях, деленных на 365.25 — примерную длину одного года в днях. Функция Int преобразует это значение в целое, и это число с помощью оператора Return возвращается в вызывающую процедуру — как и в случае с обычной функцией.

    Определение класса закончено! Вернитесь к форме Form1 и используйте новый класс в процедуре события.

    Совет. Хотя в данном примере это и не делалось, в реальном проекте полезно добавить в модуль класса логику для проверки типов данных. Это делается для того, чтобы неправильное использование свойств или методов, не приводило к возникновению ошибок времени исполнения, из-за которых выполнение программы может прерваться.

    Практическая работа №2

    Задание:

    1. Определить предметную область и сферу применения программного продукта.

    2. Определить целевую аудиторию.

    3. Построить описательную модель пользователя (профиль). При необходимости — выделить группы пользователей.

    4. Сформировать множество сценариев поведения пользователей на основании составленной модели.

    5. Выделить функциональные блоки приложения и схему навигации между ними (структуру диалога).

    6. Провести тестирование интерфейса пользователя.

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

    Тео Мандел в своей работе выделяет четыре этапа разработки пользовательского интерфейса, а именно:

    · Сбор и анализ информации от пользователей;

    · Разработка пользовательского интерфейса;

    · Построение пользовательского интерфейса;

    · Подтверждение качества пользовательского интерфейса.

    Первый шаг – определение профиля пользователя. Профиль пользователя отвечает на вопрос: «Что представляет наш пользователь?». Он позволяет нам составить представление о возрасте, образовании, предпочтениях пользователей.

    Второй шаг – анализ стоящих перед пользователями задач.

    Анализ стоящих перед пользователями задач – это определение того, чего хотят пользователи и каким образом они собираются решать свои задачи.

    Концептуальное проектирование есть определение общей структуры и взаимодействия продукта. По определению Алана Купера, концептуальные принципы проектирования «помогают определить сущность продукта и его место в более широком контексте использования, который требуется пользователям».

    Концептуальное проектирование включает:

    · Определение типа интерфейса будущего приложения (монопольный, временный, фоновый);

    · Организацию инфраструктуры взаимодействия;

    Согласно определению Алана Купера, тип интерфейса определяет поведенческую сущность продукта, то есть то, как он предъявляет себя пользователю. Тип интерфейса – это способ описать то, как много внимания пользователь будет уделять взаимодействию c продуктом, и каким образом продукт будет реагировать на это внимание.

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

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

    Интерфейс настольных приложений можно отнести к одному из трёх типов: монопольный, временный и фоновый.

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

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

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

    Инфраструктура взаимодействия включает варианты поведения приложения. Создание инфраструктуры взаимодействия предполагает выполнение шести шагов:

    Шаг 1. Определение форм-фактора, типа приложения и способов управления.

    Шаг 2. Определение функциональных и информационных элементов.

    Шаг 3. Определение функциональных групп и иерархических связей между ними.

    Шаг 4. Макетирование общей инфраструктуры взаимодействия.

    Шаг 5. Создание ключевых сценариев.

    Шаг 6. Выполнение проверочных сценариев для верификации решений.

    Форм-фактор – это зависимость вида пользовательского интерфейса от используемой технической платформы.

    Функциональные и информационные элементы – это зримые представления функций и данных, доступные пользователю посредством интерфейса. Это конкретные проявления функциональных и информационных потребностей, выявленных на стадии выработки требований.

    Информационные элементы – это, как правило, фундаментальные объекты интерактивных продуктов.

    Функциональные элементы – это операции, которые могут выполняться над информационными объектами и представляющими эти объекты элементами интерфейса. В большинстве случаев функциональные элементы представляют собой инструменты, работающие с информационными элементами, а также контейнеры, содержащие информационные элементы. 

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

    Известны два вида макетов: с жёсткой компоновкой и без компоновки.

    При этом макет с жёсткой компоновкой:

    · содержит взаимное расположение элементов и визуальную информацию о приоритетах;

    · ограничивает работу графического дизайнера.

    Для макета без компоновки характерно то, что он:

    · не содержит графического представления элементов;

    · содержит текстовое описание элементов и их приоритетов;

    · не ограничивает работу графического дизайнера.

    Сценарий определяется Аланом Купером как средство описания идеального для пользователя взаимодействия. Истоки этого понятия восходят к публикациям сообщества HCI (Human-Computer Interaction – взаимодействие человека и компьютера), где оно увязывалось с указанием на метод решения задач проектирования через конкретизацию, которая понималась как использование специально составленного рассказа, чтобы одновременно конструировать и иллюстрировать проектные решения. Применение сценарного подхода к проектированию, как показано в книге Кэрролла «Making Use» (Carroll, 2000), сосредоточено на описании того, как пользователи решают задачи. Такое описание включает характеристику обстановки рабочей среды, а также агентов, или действующих лиц, которые являются абстрактными представителями пользователей.

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

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

    Шаг 1. Постановка задач и определение образа продукта.

    Шаг 2. Мозговой штурм.

    Шаг 3. Выявление ожиданий персонажей.

    Шаг 4. Разработка контекстных сценариев.

    Шаг 5. Выявление требований.

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

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

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

    Выполнение работы:

    В качестве основы для выполнения данной лабораторной работы предлагается использовать одно из ранее разработанных ими приложений.

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

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

    · Социальные и демографические характеристики (возраст, пол, основной язык, род занятий, потребности, привычки и т.п.).

    · Уровень компьютерной грамотности.

    · Цель и задачи, решаемые пользователем.

    · Окружение (рабочее место, конфигурация оборудования, используемая операционная система и т.п.)

    · Требования, специфичные для конкретной целевой группы.

    После выделения одного или нескольких основных профилей пользователей и определения задач, стоящих перед ними, переходят к следующему этапу проектирования. Он связан с составлением пользовательских сценариев. Сценарий — это описание действий, выполняемых пользователем в рамках решения конкретной задачи на пути достижения его цели. Очевидно, что достигнуть некоторой цели можно, решая ряд задач. Каждую их них пользователь может решать несколькими способами, следовательно, должно быть сформировано несколько сценариев. Чем больше их будет, тем ниже вероятность того, что некоторые ключевые объекты и операции будут упущены.

    Требования к отчету: Текст должен быть написан шрифтом Times New Roman, 12. Интервал между строками и абзацами – 1,5. Отступ слева 1,5. Ориентация текста – по ширине страницы. Скриншоты необходимо подписать. Название практической работы, цель работы, ход работы, вывод, ответы на контрольные вопросы, должны быть выделены жирным шрифтом, так же как в методичке.

    Контрольные вопросы:

    1. Что такое интерфейс?

    2. Какие типы пользовательских интерфейсов существуют?

    3. Перечислите этапы разработки пользовательских интерфейсов?

    4. К какому типу интерфейсов будет относиться интерфейс, разработанный в данной лабораторной работе?

    5. Какие модели интерфейсов существуют?

    6. Какая модель интерфейса будет использована в данной работе?

    7. Что такое диалог?

    8. Какие типы диалогов существуют?

    9. Какие формы диалога Вы знаете?

    10. Какой тип диалога и какая форма диалога будет использована в данной работе?

    Практическая работа №3

    Задание:

    1)    Ознакомиться с лекционным материалом по теме «Тестирование, отладка и ПО»

    2)    Провести функциональное тестирование разработанного программного обеспечения

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

    Процесс тестирования состоит из трёх этапов:

    1. Проектирование тестов.

    2. Исполнение тестов.

    3. Анализ полученных результатов.

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

    Виды тестов

    Функциональные тесты составляются на уровне спецификации, до решения задачи. Будущий алгоритм рассматривается как «черный ящик» — функция с неизвестной (или не рассматриваемой) структурой, преобразующая входы в выходы. Суть функциональных тестов: каким бы способом ни решалась задача, при заданных входных значениях должны получиться соответствующие выходные значения.

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

    Чаще всего совокупность тщательно составленных функциональных тестов покрывает множество структурных тестов.

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

    Общая последовательность разработки тестов

    Наиболее рациональная процедура заключается в том, что сначала разрабатываются функциональные тесты, а затем – структурные.

    Функциональное тестирование (тестирование «черного ящика»)

    При функциональном тестировании выявляются следующие категории ошибок:

    · некорректность или отсутствие функций;

    · ошибки интерфейса;

    · ошибки в структурах данных;

    · ошибки машинных характеристик (нехватка памяти и др.);

    · ошибки инициализации и завершения.

    Техника тестирования ориентирована:

    · на сокращение необходимого количества тестовых вариантов;

    · на выявление классов ошибок, а не отдельных ошибок.

    Способы функционального тестирования

    Разбиение на классы эквивалентности

    Это самый популярный способ. Его суть заключается в разделении области входных данных программы на классы эквивалентности и разработке для каждого класса одного тестового варианта.

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

    Классы эквивалентности определяются по спецификации программы. Тесты строятся в соответствии с классами эквивалентности, а именно: выбирается вариант исходных данных некоторого класса и определяются соответствующие выходные данные.

    Самыми общими классами эквивалентности являются классы допустимых и недопустимых (аномальных) исходных данных. Описание класса строится как комбинация условий, описывающих каждое входное данное.

    Условия допустимости или недопустимости данных задают возможные значения данных и могут описывать:

    · некоторое конкретное значение; определяется один допустимый и два недопустимых класса эквивалентности: заданное значение, множество значений меньше заданного, множество значений больше заданного;

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

    · множество конкретных значений; определяется один допустимый и один недопустимый класс эквивалентности: заданное множество и множество значений, в него не входящих.

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

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

    Анализ граничных значений

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

    Основные правила построения тестов:

    · если условие правильности данных задает диапазон, то строятся тесты для левой и правой границы диапазона; для значений чуть левее левой и чуть правее правой границы;

    · если условие правильности данных задает дискретное множество значений, то строятся тесты для минимального и максимального значений; для значений чуть меньше минимума и чуть больше максимума;

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

    · Диаграммы причин-следствий

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

    Выполнение работы:

    1)    Разработать тестовые наборы для функционального тестирования.

    2)    Провести тестирование программы и представить результаты в виде таблицы.

    3)    Выработать рекомендации для корректировки тестируемой программы.

    4)    Представить отчет по лабораторной работе для защиты.

    Требования к отчету: Текст должен быть написан шрифтом Times New Roman, 12. Интервал между строками и абзацами – 1,5. Отступ слева 1,5. Ориентация текста – по ширине страницы. Скриншоты необходимо подписать. Название практической работы, цель работы, ход работы, вывод, ответы на контрольные вопросы, должны быть выделены жирным шрифтом, так же как в методичке.

    Контрольные вопросы:

    1.    Что такое тестирование ПС?

    2.    Чем тестирование отличается от отладки ПС?

    3.    Для чего проводится функциональное тестирование?

    4.    Каковы правила тестирования программы «как черного ящика»?

    5.    Как проводится тестирования программы по принципу «белого ящика»?

    6.    Как осуществляется сборка программы при модульно тестировании?

    Практическая работа №4

    Практическая работа №5

    Задание:

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

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

    Оптимизация – преобразование программы, сохраняющее ее семантику (конструкции языка программирования), но уменьшающие ее размер и время выполнения.

    Виды оптимизация программы:

    · глобальная (всей программы);

    · локальная (нескольких соседних операторов, образующих линейный участок);

    · квазилокальная (фрагментов программы фиксированной структуры, например, циклов).

    Способы оптимизации:

    1. Разгрузка участков повторяемости: вынесение вычислений из многократно проходимых исполняемых участков программы на участки программы, редко проходимые. Таким образом, это преобразование тела цикла или рекурсивных процедур.

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

    а) упрощение действий происходит при замене сложных операций в выражениях более простыми: x / 0.4 -> x*0.25;

    б) преобразование по объединению или расчленению циклов, по перестановке заголовков циклов, по удалению избыточных выражений (замене их на переменную).

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

    Y=F(W), X=Y на X=F(W)

    4. Чистка программы (удаление ненужных конструкций): недостижимых операторов, существенных операторов, неиспользуемых переменных, видов, операций.

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

    6. Экономия памяти -уменьшение объема памяти, отводимые под информационные объекты программы (например, параметры процедуры).

    Выполнение работы:

    Поможем в ✍️ написании учебной работы


    Учебное заведение: ТОГБПОУ
    Населённый пункт: г.Уварово
    Наименование материала: Комплект контрольно-оценочных средств
    Тема: Комплект контрольно-оценочных средств по профессиональному модулю ПМ 3 Ревьюирование программных продуктов

    Тамбовское областное государственное бюджетное

    профессиональное образовательное учреждение

    «Уваровский химико-технологический колледж»

    РАССМОТРЕНО ОДОБРЕНО:

    Предметно-цикловой комиссией

    _информационных технологий_

    Протокол №_______________

    от «__»_______________ 2019г.

    Председатель ПЦК____________

    УТВЕРЖДАЮ:

    Директор ТОГБПОУ УХТК

    ____________Н.А.Ермакова

    «__»_______________ 2019г.

    Комплект контрольно-оценочных средств по профессиональному модулю

    ПМ 3 Ревьюирование программных продуктов

    основной профессиональной образовательной программы (ОПОП) по

    направлению подготовки (специальности)

    по специальности СПО

    09.02.07 «Информационные системы и программирование»

    (код, название)

    1

    СОДЕРЖАНИЕ

    1 ПАСПОРТ КОНТРОЛЬНО-ИЗМЕРИТЕЛЬНЫХ МАТЕРИАЛОВ ……………………….3

    2 КОМПЛЕКТ МАТЕРАЛОВ ДЛЯ ТЕКУЩЕЙ АТТЕСТАЦИИ ……………………………..5

    3КОМПЛЕКТ МАТЕРАЛОВ ДЛЯ ПРОМЕЖУТОЧНОЙ АТТЕСТАЦИИ ………….10

    2

    1 ПАСПОРТ КОНТРОЛЬНО-ИЗМЕРИТЕЛЬНЫХ МАТЕРИАЛОВ

    Контрольно-

    измерительные

    материалы

    предназначены

    для

    проверки

    результатов

    освоения

    профессионального модуля ПМ.03 Ревьюирование программных продуктов программы

    подготовки специалистов среднего звена по специальности 09.02.07 Информационные

    системы и программирование. Контрольно-измерительные материалы позволяют оценивать

    освоение умений и усвоение знаний по междисциплинарному курсу.

    1.1 Контроль и оценка результатов освоения

    Результаты обучения (освоенные умения,

    усвоенные знания)

    Формы и методы контроля и оценки

    результатов обучения

    В результате изучения профессионального

    модуля обучающийся должен уметь:

    работать с проектной документацией,

    разработанной с использованием

    графических языков спецификаций;

    • выполнять оптимизацию программного

    кода с использованием специализированных

    программных средств;

    • использовать методы и технологии

    тестирования и ревьюирования кода и

    проектной документации;

    • применять стандартные метрики по

    прогнозированию затрат, сроков и качества

    Тестирование

    Практическое задание

    Дифференцированный зачет

    В результате изучения профессионального

    модуля обучающийся должен

    Знать:

    • задачи планирования и контроля развития

    проекта;

    • принципы построения системы

    деятельностей программного проекта;

    • современные стандарты качества

    программного продукта и процессов его

    обеспечения

    Тестирование

    Практическое задание

    Дифференцированный зачет

    1.2 Организация промежуточного контроля

    Промежуточный контроль освоения междисциплинарного курса осуществляется в форме

    дифференцированного зачёта.

    Дифференцированный зачет проводится в виде устного ответа на теоретические вопросы

    и выполнения практического задания на компьютере

    1.3. Освоение профессиональных и общих компетенций

    Наименование результата обучения

    Средства проверки

    ПК 3.1 Осуществлять ревьюирование

    программного кода в соответствии с

    технической документацией

    Вопросы для текущего контроля

    Тесты

    Практическое задание

    ПК.3.2 Выполнять измерение

    характеристик компонент программного

    продукта для определения соответствия

    заданным критериям

    Вопросы для текущего контроля

    Тесты

    Практическое задание

    ПК.3.3 Производить исследование

    Вопросы для текущего контроля

    3

    созданного программного кода с

    использованием специализированных

    программных средств с целью выявления

    ошибок и отклонения от алгоритма

    Тесты

    Практическое задание

    ПК.3.4 Проводить сравнительный анализ

    программных продуктов и средств

    разработки, с целью выявления наилучшего

    решения согласно критериям,

    определенным техническим заданием

    Вопросы для текущего контроля

    Тесты

    Практическое задание

    ОК 1 Выбирать способы решения задач

    профессиональной деятельности,

    применительно к различным контекстам

    Вопросы для текущего контроля

    Тесты

    Практическое задание

    ОК 2 Осуществлять поиск, анализ и

    интерпретацию информации, необходимой

    для выполнения задач профессиональной

    деятельности

    Вопросы для текущего контроля

    Тесты

    Практическое задание

    ОК 3 Планировать и реализовывать

    собственное профессиональное и

    личностное развитие

    Вопросы для текущего контроля

    Тесты

    Практическое задание

    ОК 4 Работать в коллективе и команде,

    эффективно взаимодействовать с

    коллегами, руководством, клиентами

    Вопросы для текущего контроля

    Тесты

    Практическое задание

    ОК 5 Осуществлять устную и письменную

    коммуникацию на государственном языке с

    учетом особенностей социального и

    культурного контекста.

    Вопросы для текущего контроля

    Тесты

    Практическое задание

    ОК 6 Проявлять гражданско-

    патриотическую позицию, демонстрировать

    осознанное поведение на основе

    традиционных общечеловеческих

    ценностей

    Вопросы для текущего контроля

    Тесты

    Практическое задание

    ОК 7 Содействовать сохранению

    окружающей среды, ресурсосбережению,

    эффективно действовать в чрезвычайных

    ситуациях

    Вопросы для текущего контроля

    Тесты

    Практическое задание

    ОК 8 Использовать средства физической

    культуры для сохранения и укрепления

    здоровья в процессе профессиональной

    деятельности и поддержания необходимого

    уровня физической подготовленности

    Вопросы для текущего контроля

    Тесты

    Практическое задание

    ОК 9 Использовать информационные

    технологии в профессиональной

    деятельности

    Вопросы для текущего контроля

    Тесты

    Практическое задание

    ОК 10 Пользоваться профессиональной

    документацией на государственном и

    иностранном языке

    Вопросы для текущего контроля

    Тесты

    Практическое задание

    ОК 11 Планировать предпринимательскую

    деятельность в профессиональной сфере

    Вопросы для текущего контроля

    Тесты

    Практическое задание

    4

    2

    КОМПЛЕКТ МАТЕРИАЛОВ ДЛЯ ТЕКУЩЕЙ АТТЕСТАЦИИ

    Информационная модель – это

    Процесс построения информационных

    моделей с помощью формальных языков

    называют

    имитационное моделирование применяют

    в случаях

    метод построения информационных систем

    Многокомпонентная система

    Автоматизированная система

    моделирования (АСМ)

    В основе Объектно-ориентированого

    подхода лежат понятия

    жизненный цикл Объектно-

    ориентированого подхода

    проектирование

    Для чего нужно моделирование

    программного обеспечения

    Информационная модель – это

    Для создания описательных текстовых

    информационных моделей обычно

    используют

    Модели, построенные с использованием

    математических понятий и формул,

    называют

    метод построения информационных систем

    Метод “снизу-вверх”

    Каскадная модель ИС

    Автоматизированная система

    моделирования (АСМ) Функциональное

    наполнение

    жизненный цикл Объектно-

    ориентированого подхода

    Программирование, тестирование и

    сборка системы

    Для чего нужно моделирование

    программного обеспечения

    Информационная модель – это

    Основное отличие формальных языков от

    естественных состоит

    Концептуально-методологическое

    моделирование представляет собой

    метод построения информационных систем

    Метод “сверху-вниз”

    Поэтапная (итерационная) модель ИС с

    промежуточным контролем

    Автоматизированная система

    моделирования (АСМ) Системное

    наполнение

    жизненный цикл Объектно-

    ориентированого подхода анализ

    5

    Для чего нужно моделирование

    программного обеспечения

    Информационная модель – это

    Модели, построенные с использованием

    математических понятий и формул,

    называют

    метод построения информационных систем

    Принципы “дуализма” и

    многокомпонентности

    Концептуально-методологическое

    моделирование представляет собой

    имитационное моделирование применяют

    в случаях

    Автоматизированная система

    моделирования (АСМ) Язык заданий (ЯЗ)

    Преимущества Объектно-

    ориентированого подхода Повторное

    использование

    Для чего нужно моделирование

    программного обеспечения

    Информационная модель – это

    Концептуально-методологическое

    моделирование представляет собой

    метод построения информационных систем

    Принципы “дуализма” и

    многокомпонентности

    метод построения информационных систем

    Многокомпонентная система

    Автоматизированная система

    моделирования (АСМ) Язык заданий (ЯЗ)

    Преимущества Объектно-

    ориентированого подхода

    Распараллеливание работ

    жизненный цикл Объектно-

    ориентированого подхода

    Программирование, тестирование и

    сборка системы

    Для чего нужно моделирование

    программного обеспечения

    Информационная модель – это

    С помощью формальных языков строят

    информационные модели определённого

    типа

    Концептуальное

    моделирование представляет собой

    Каскадная модель ИС

    В основе Объектно-ориентированого

    подхода лежат понятия

    Преимущества Объектно-

    ориентированого подхода Повторное

    6

    использование

    жизненный цикл Объектно-

    ориентированого подхода анализ

    Для чего нужно моделирование

    программного обеспечения

    Информационная модель – это

    Наряду с естественными языками (русский,

    английский и т.д.) разработаны и

    используются формальные языки:

    Процесс построения информационных

    моделей с помощью формальных языков

    называют

    имитационное моделирование на ЭВМ

    метод построения информационных систем

    Метод “сверху-вниз”

    Поэтапная (итерационная) модель ИС с

    промежуточным контролем

    Преимущества Объектно-

    ориентированого подхода Повторное

    использование

    Для чего нужно моделирование

    программного обеспечения

    Информационная модель – это

    Модели, построенные с использованием

    математических понятий и формул,

    называют

    Концептуальное

    моделирование представляет собой

    имитационное моделирование применяют

    в случаях

    метод построения информационных систем

    Многокомпонентная система

    Автоматизированная система

    моделирования (АСМ)

    Преимущества Объектно-

    ориентированого подхода

    Распараллеливание работ

    Для чего нужно моделирование

    программного обеспечения

    Ролевой состав коллектива разработчиков,

    взаимодействие между ролями в различных

    технологических процессах

    Заказчик (заявитель).

    Менеджер проекта

    Менеджер программы

    Разработчик.

    Специалист по тестированию

    Специалист по контролю качества.

    Специалист по сертификации.

    7

    Специалист по внедрению и

    сопровождению

    Специалист по безопасности

    Инструктор

    Технический писатель

    цели верификации

    Задачи процесса верификации

    Основная идея в тестировании системы, как

    черного ящика состоит в том, что

    С точки зрения программного кода черный

    ящик может представлять собой

    При тестировании системы, как

    стеклянного ящика, тестировщик имеет

    доступ

    Тестирование моделей позволяет

    тестировщику

    Анализ программного кода (инспекции)

    производится в случае когда

    Тестовое окружение может использоваться

    для

    Целью тестирования тестового окружения

    является

    Тестовые вопросы.

    1.Модель может быть построена для любого

    Вариант1= объекта, явления или процесса

    Вариант2= объекта или процесса

    Вариант3= объекта или явления

    Вариант4= объекта

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

    существенные признаки, называется

    Вариант1= реализацией

    Вариант2= упрощением

    Вариант3= микромоделированием

    Вариант4= моделированием

    3.Формализация в процессе моделирования это

    Вариант1= процесс замещения оригинала его аналога

    Вариант2= комплексирование с уже имеющимися реальными системами

    Вариант3= проектирование и настройка модели

    Вариант4= постановка различных задач и решение их на модели

    4.Процесс моделирования это

    Вариант1= постановка различных задач и решение их на модели

    Вариант2= проектирование и настройка модели

    Вариант3= комплексирование с уже имеющимися реальными системами

    Вариант4= процесс замещения оригинала его аналога

    5.Процесс интерпретации результатов моделирования это

    Вариант1= постановка различных задач и решение их на модели

    Вариант2= проектирование и настройка модели

    Вариант3= комплексирование с уже имеющимися реальными системами

    Вариант4= процесс замещения оригинала его аналога

    8

    6.Принцип моделирования ПО – правильно выбранная модель позволит проникнуть в

    суть задачи, неправильная модель заведет в тупик

    Вариант1= возможность рассматривать систему на разных уровнях детализации

    Вариант2= модели могут создаваться и изучаться по отдельности, но остаются

    взаимосвязанными

    Вариант3= выбор модели оказывает влияние на решение проблем

    Вариант4= необходимо объединить все независимые представления системы в единое целое

    7.Принцип моделирования ПО– каждая модель может быть представлена с различной

    степенью точности

    Вариант1= модели могут создаваться и изучаться по отдельности, но остаются

    взаимосвязанными

    Вариант2= возможность рассматривать систему на разных уровнях детализации

    Вариант3= необходимо объединить все независимые представления системы в единое целое

    Вариант4= выбор модели оказывает влияние на решение проблем

    8.Принцип моделирования ПО — лучшие модели те, что ближе к реальности

    Вариант1= выбор модели оказывает влияние на решение проблем

    Вариант2= модели могут создаваться и изучаться по отдельности, но остаются

    взаимосвязанными

    Вариант3= возможность рассматривать систему на разных уровнях детализации

    Вариант4= необходимо объединить все независимые представления системы в единое целое

    9.Принцип моделирования ПО – нельзя ограничиваться созданием только одной модели

    Вариант1= необходимо объединить все независимые представления системы в единое целое

    Вариант2= возможность рассматривать систему на разных уровнях детализации

    Вариант3= выбор модели оказывает влияние на решение проблем

    Вариант4= модели могут создаваться и изучаться по отдельности, но остаются

    взаимосвязанными

    10.Алгоритмический подход моделирования при разработке ПО

    Вариант1= основным строительным блоком является процедура или функция

    Вариант2= основным строительным блоком является объект или класс

    11.Объектно-ориентированный подход моделирования при разработке ПО

    Вариант1= основным строительным блоком является процедура или функция

    Вариант2= основным строительным блоком является объект или класс

    12.Свойство объектов и классов при объектно-ориентированном моделировании ПО

    инкапсуляция — это

    Вариант1= скрытие объектом внутренней информации от внешнего мира

    Вариант2= возможность создавать из классов новые классы по принципу «от общего к

    частному»

    13.Свойство объектов и классов при объектно-ориентированном моделировании ПО –

    наследование это

    Вариант1= скрытие объектом внутренней информации от внешнего мира

    Вариант2= возможность создавать из классов новые классы по принципу «от общего к

    частному»

    14.Преимущество методологии объектно-ориентированного программирования —

    повторное использование

    Вариант1= когда изменение носит характер уточнения, детализации, вводятся новые классы,

    наследующие поведение ранее созданных

    Вариант2= при наличии развитых библиотек классов проектирование и программирование

    новых приложений будет в основном сводиться к сборке системы из готовых фрагментов.

    Вариант3= программирование и тестирование отдельных компонент системы возможно до

    завершения проектирования целевой программной системы

    15.Преимущество методологии объектно-ориентированного программирования —

    упрощение внесения изменений

    9

    Вариант1= при наличии развитых библиотек классов проектирование и программирование

    новых приложений будет в основном сводиться к сборке системы из готовых фрагментов

    Вариант2= когда изменение носит характер уточнения, детализации, вводятся новые классы,

    наследующие поведение ранее созданных

    Вариант3= программирование и тестирование отдельных компонент системы возможно до

    завершения проектирования целевой программной системы

    16.Преимущество методологии объектно-ориентированного программирования —

    распараллеливание работ

    Вариант1= когда изменение носит характер уточнения, детализации, вводятся новые классы,

    наследующие поведение ранее созданных

    Вариант2= программирование и тестирование отдельных компонент системы возможно до

    завершения проектирования целевой программной системы

    Вариант3= при наличии развитых библиотек классов проектирование и программирование

    новых приложений будет в основном сводиться к сборке системы из готовых фрагментов

    Вариант3= при наличии развитых библиотек классов проектирование и программирование

    новых приложений будет в основном сводиться к сборке системы из готовых фрагментов

    17.Ролевой состав коллектива разработчиков. Заказчик (заявитель).

    Вариант1= принимает технические решения, которые могут быть реализованы и

    использованы, создает продукт, удовлетворяющий спецификациям

    Вариант2= ограничен в своем взаимодействии и общается только с менеджерами проекта и

    специалистом по сертификации или внедрению.

    Вариант3= приводит документацию на программную систему в соответствие требованиям

    сертифицирующего органа

    Вариант4= Участвует в анализе особенностей площадки заказчика, на которой планируется

    проводить внедрение разрабатываемой системы

    18.Ролевой состав коллектива разработчиков. Менеджер проекта.

    Вариант1= обеспечивает коммуникационный канал между заказчиком и проектной группой

    Вариант2= принимает технические решения, которые могут быть реализованы и

    использованы, создает продукт, удовлетворяющий спецификациям

    Вариант3= ограничен в своем взаимодействии и общается только с менеджерами проекта и

    специалистом по сертификации или внедрению

    Вариант4= приводит документацию на программную систему в соответствие требованиям

    сертифицирующего органа

    19.Ролевой состав коллектива разработчиков. Менеджер программы

    Вариант1= принимает технические решения, которые могут быть реализованы и

    использованы, создает продукт, удовлетворяющий спецификациям

    Вариант2= ограничен в своем взаимодействии и общается только с менеджерами проекта и

    специалистом по сертификации или внедрению

    Вариант3= управляет коммуникациями и взаимоотношениями в проектной группе, является

    координатором

    Вариант4= приводит документацию на программную систему в соответствие требованиям

    сертифицирующего органа

    20.Ролевой состав коллектива разработчиков. Разработчик.

    Вариант1= принимает технические решения, которые могут быть реализованы и

    использованы, создает продукт, удовлетворяющий спецификациям

    Вариант2= ограничен в своем взаимодействии и общается только с менеджерами проекта и

    специалистом по сертификации или внедрению

    Вариант3= Участвует в анализе особенностей площадки заказчика, на которой планируется

    проводить внедрение разрабатываемой системы

    Вариант4= несет обязанности по подготовке документации к разработанному

    21. Ролевой состав коллектива разработчиков. Специалист по тестированию.

    Вариант1= несет обязанности по подготовке документации к разработанному продукту

    10

    Вариант2= Участвует в анализе особенностей площадки заказчика, на которой планируется

    проводить внедрение разрабатываемой системы

    Вариант3= ограничен в своем взаимодействии и общается только с менеджерами проекта и

    специалистом по сертификации или внедрению

    Вариант4= определяет стратегию тестирования, тест-требования и тест-планы для каждой из

    фаз проекта

    22.Ролевой состав коллектива разработчиков. Специалист по контролю качества.

    Вариант1= осуществляет взаимодействие с разработчиком, менеджером программы и

    специалистами по безопасности и сертификации

    Вариант2= участвует в анализе особенностей площадки заказчика, на которой планируется

    проводить внедрение разрабатываемой системы

    Вариант3= несет обязанности по подготовке документации к разработанному продукту

    Вариант4= приводит документацию на программную систему в соответствие требованиям

    сертифицирующего органа

    23.Ролевой состав коллектива разработчиков. Специалист по сертификации.

    Вариант1= Участвует в анализе особенностей площадки заказчика, на которой планируется

    проводить внедрение разрабатываемой системы

    Вариант2= несет обязанности по подготовке документации к разработанному продукту

    Вариант3= обеспечивает коммуникационный канал между заказчиком и проектной группой

    Вариант4= приводит документацию на программную систему в соответствие требованиям

    сертифицирующего органа

    24.Ролевой состав коллектива разработчиков. Специалист по внедрению и

    сопровождению.

    Вариант1= несет обязанности по подготовке документации к разработанному продукту

    Вариант2= участвует в анализе особенностей площадки заказчика, на которой планируется

    проводить внедрение разрабатываемой системы

    Вариант3= приводит документацию на программную систему в соответствие требованиям

    сертифицирующего органа

    Вариант4= обеспечивает коммуникационный канал между заказчиком и проектной группой

    25.Ролевой состав коллектива разработчиков. Специалист по безопасности.

    Вариант1= ответственен за весь спектр вопросов безопасности создаваемого продукта

    Вариант2= участвует в анализе особенностей площадки заказчика, на которой планируется

    проводить внедрение разрабатываемой системы

    Вариант3= несет обязанности по подготовке документации к разработанному продукту

    Вариант4= приводит документацию на программную систему в соответствие требованиям

    сертифицирующего органа

    26.Ролевой состав коллектива разработчиков. Инструктор.

    Вариант1= отвечает за снижение затрат на дальнейшее сопровождение продукта, обеспечение

    максимальной эффективности работы пользователя

    Вариант2= Участвует в анализе особенностей площадки заказчика, на которой планируется

    проводить внедрение разрабатываемой системы

    Вариант3= приводит документацию на программную систему в соответствие требованиям

    сертифицирующего органа

    Вариант4= обеспечивает коммуникационный канал между заказчиком и проектной группой

    27.Ролевой состав коллектива разработчиков. Технический писатель.

    Вариант1= участвует в анализе особенностей площадки заказчика, на которой планируется

    проводить внедрение разрабатываемой системы

    Вариант2= обеспечивает коммуникационный канал между заказчиком и проектной группой

    Вариант3= управляет коммуникациями и взаимоотношениями в проектной группе, является

    координатором

    Вариант4= несет обязанности по подготовке документации к разработанному продукту

    28.Тестирование программного обеспечения

    Вариант1= проверка соответствия системы ожиданиям заказчика

    11

    Вариант2= включает в себя инспекции, тестирование кода, анализ результатов тестирования,

    формирование и анализ отчетов о проблемах

    Вариант3= управляемое выполнение программы с целью обнаружения несоответствий ее

    поведения и требований

    29.Верификация программного обеспечения

    Вариант1= включает в себя инспекции, тестирование кода, анализ результатов тестирования,

    формирование и анализ отчетов о проблемах

    Вариант2= управляемое выполнение программы с целью обнаружения несоответствий ее

    поведения и требований

    Вариант3= проверка соответствия системы ожиданиям заказчика

    Вариант4=

    30.Валидация программной системы

    Вариант1= проверка соответствия системы ожиданиям заказчика

    Вариант2= управляемое выполнение программы с целью обнаружения несоответствий ее

    поведения и требований

    Вариант3= включает в себя инспекции, тестирование кода, анализ результатов тестирования,

    формирование и анализ отчетов о проблемах

    31.Модульному тестированию подвергаются

    Вариант1= система целиком

    Вариант2= небольшие модули

    Вариант3= тестируется вся программа в целом

    32.Интеграционное тестирование

    Вариант1= когда выполняется сборка отдельных модулей в более крупные конфигурации

    Вариант2= тестируются небольшие модули

    Вариант3= тестируется система в целом

    33. Системное тестирование

    Вариант1= тестируется конфигурация сборки отдельных модулей в более крупные

    Вариант2= тестируются небольшие модули

    Вариант3= тестируется система в целом

    34.Целью тестов для нормальных ситуаций является

    Вариант1= демонстрация способности программного обеспечения адекватно реагировать на

    ненормальные входы и условия

    Вариант2= демонстрация способности программного обеспечения давать отклик на

    нормальные входы и условия в соответствии с требованиями.

    35.Целью тестов для ненормальных ситуаций является

    Вариант1= демонстрация способности программного обеспечения давать отклик на

    нормальные входы и условия в соответствии с требованиями

    Вариант2= демонстрация способности программного обеспечения адекватно реагировать на

    ненормальные входы и условия

    36.Сертификация ПО

    Вариант1= процесс установления того, что разработка ПО проводилась в соответствии с

    определенными требованиями

    Вариант2= процесс установления и официального признания того, что разработка ПО

    проводилась в соответствии с определенными требованиями

    Вариант3= процесс официального признания того, что разработка ПО проводилась в

    соответствии с определенными требованиями

    37.Если сертификация направлена на получение сертификата соответствия

    Вариант1= то результатом сертификации является признание соответствия процессов

    разработки определенным критериям, а функциональности системы определенным

    требованиям.

    Вариант2= то результатом является признание соответствия процессов разработки

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

    продукции

    12

    38.Если сертификация направлена на получение сертификата качества

    Вариант1= то результатом является признание соответствия процессов разработки

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

    продукции

    Вариант2= то результатом сертификации является признание соответствия процессов

    разработки определенным критериям, а функциональности системы определенным

    требованиям

    39.Тестирование программного кода это процесс

    Вариант1= выполнения программного кода, направленный на исправление существующих в

    нем дефектов

    Вариант2= выполнения программного кода, направленный на выявление существующих в нем

    дефектов

    40.Под дефектом программного кода понимается

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

    приводит к ожидаемому поведению системы

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

    приводит к неожиданному поведению системы

    41.В задачи тестирования

    Вариант1= не входит выявление конкретных дефектных участков программного кода

    Вариант2= входит выявление конкретных дефектных участков программного кода

    Вариант3= входит исправление дефектов

    Вариант4= не входит выявление конкретных дефектных участков программного кода, но

    входит исправление дефектов

    42.Цель применения процедуры тестирования программного кода

    Вариант1= оптимизация количества дефектов в конечном продукте

    Вариант2= минимизация количества дефектов в конечном продукте.

    Вариант3= декомпозиция количества дефектов в конечном продукте

    43. Метод функциональной декомпозиции состоит в том, что

    Вариант1= система разбивается на отдельные модули– выполняется модульное тестирование,

    затем выполняется интеграционное тестирование и после этого выполняется системное

    тестирование.

    Вариант2= система разбивается на отдельные модули– выполняется модульное тестирование,

    затем выполняется системное тестирование.

    Вариант3= система разбивается на отдельные модули– выполняется модульное тестирование,

    затем выполняется интеграционное тестирование

    Вариант4= система разбивается на отдельные модули– выполняется модульное тестирование,

    затем выполняется системное тестирование и после этого выполняется интеграционное

    тестирование.

    44.Робастность системы

    Вариант1= управляемое выполнение программы с целью обнаружения несоответствий ее

    поведения и требований

    Вариант2= включает в себя инспекции, тестирование кода, анализ результатов тестирования,

    формирование и анализ отчетов о проблемах

    Вариант3= проверка соответствия системы ожиданиям заказчика

    Вариант4= это степень ее чувствительности к факторам, не учтенным на этапах ее

    проектирования

    45.Разбиение на классы эквивалентности

    Вариант1= способ увеличения необходимого числа тестовых примеров.

    Вариант2= способ уменьшения необходимого числа тестовых примеров.

    46.Если различные входные значения приводят к одним и тем же реакциям системы

    Вариант1= то невозможно объединение таких значений в классы эквивалентности

    Вариант2= то возможно объединение таких значений в классы эквивалентности

    47.Разбиение на классы эквивалентности особенно полезно

    13

    Вариант1= когда на вход системы может быть подано большое количество различных

    значений

    Вариант2= когда на вход системы может быть подано малое количество различных значений

    Вариант3= когда на вход системы не может быть подано большое количество различных

    значений

    48.Тест-план представляет собой

    Вариант1= документ, в котором перечислены часть тестовых примеров, необходимых для

    тестирования системы

    Вариант2= документ, в котором перечислены все тестовые примеры, необходимые для

    тестирования системы

    49.В каждом тестовом примере обязательно перечислены

    Вариант1= все входные значения а также сценарий, описывающий последовательность

    действий, которые необходимо выполнить тестовому окружению для выполнения тестового

    примера

    Вариант2= все входные значения и ожидаемые выходные значения, а также сценарий,

    описывающий последовательность действий, которые необходимо выполнить тестовому

    окружению для выполнения тестового примера

    Вариант3= все входные значения и ожидаемые выходные значения

    50.Тест считается пройденным

    Вариант1= если ожидаемые и реальные выходные значения совпадают

    Вариант2= если ожидаемые и реальные выходные значения не совпадают

    Вариант3= если система выдала выходные значения, которые не ожидались

    51.Одна из оценок качества системы тестов — это ее полнота, т.е.

    Вариант1= величина той части функциональности системы, которая не проверяется тестовыми

    примерами

    Вариант2= величина той части функциональности системы, которая проверяется тестовыми

    примерами

    52.Покрытие требований позволяет оценить

    Вариант1= полноту по отношению к программной реализации системы

    Вариант2= степень полноты системы тестов по отношению к функциональности системы

    Вариант3= каждая компонента логического условия в результате выполнения тестовых

    примеров должна принимать все возможные значения

    53.Уровни покрытия. По строкам программного кода (Statement Coverage)

    Вариант1= в результате выполнения тестов каждый оператор был выполнен хотя бы один раз

    Вариант2= для обеспечения полного покрытия необходимо, чтобы как логическое условие,

    так и каждая его компонента приняла все возможные значения

    Вариант3= каждая компонента логического условия в результате выполнения тестовых

    примеров должна принимать все возможные значения

    Вариант4= метод учитывающий структуру компонент условий и значения, которые они

    принимают при выполнении тестовых примеров

    54.Уровни покрытия. По веткам условных операторов (Decision Coverage)

    Вариант1= метод учитывающий структуру компонент условий и значения, которые они

    принимают при выполнении тестовых примеров

    Вариант2= каждая точка входа и выхода в программе и во всех ее функциях должна быть

    выполнена по крайней мере один раз и все логические выражения в программе должны

    принять каждое из возможных значений хотя бы один раз

    Вариант3= должны быть проверены все возможные наборы значений компонент логических

    условий

    Вариант4= в результате выполнения тестов каждый оператор был выполнен хотя бы один раз

    55.Уровни покрытия. По компонентам логических условий

    Вариант1= в результате выполнения тестов каждый оператор был выполнен хотя бы один раз

    Вариант2= метод учитывающий структуру компонент условий и значения, которые они

    принимают при выполнении тестовых примеров

    14

    Вариант3= должны быть проверены все возможные наборы значений компонент логических

    условий

    Вариант4= каждая компонента логического условия в результате выполнения тестовых

    примеров должна принимать все возможные значения

    56.Уровни покрытия. Покрытие по условиям (Condition Coverage)

    Вариант1= каждая компонента логического условия в результате выполнения тестовых

    примеров должна принимать все возможные значения

    Вариант2= в результате выполнения тестов каждый оператор был выполнен хотя бы один раз

    Вариант3= должны быть проверены все возможные наборы значений компонент логических

    условий

    Вариант4= для обеспечения полного покрытия необходимо, чтобы как логическое условие,

    так и каждая его компонента приняла все возможные значения

    57.Уровни покрытия. Покрытие по веткам/условиям (Condition/Decision Coverage)

    Вариант1= для обеспечения полного покрытия необходимо, чтобы как логическое условие,

    так и каждая его компонента приняла все возможные значения

    Вариант2= должны быть проверены все возможные наборы значений компонент логических

    условий

    Вариант3= метод учитывающий структуру компонент условий и значения, которые они

    принимают при выполнении тестовых примеров

    Вариант4= в результате выполнения тестов каждый оператор был выполнен хотя бы один раз

    58.Уровни покрытия. Покрытие по всем условиям (Multiple Condition Coverage)

    Вариант1= в результате выполнения тестов каждый оператор был выполнен хотя бы один раз

    Вариант2= должны быть проверены все возможные наборы значений компонент логических

    условий

    Вариант3= для обеспечения полного покрытия необходимо, чтобы как логическое условие,

    так и каждая его компонента приняла все возможные значения

    Вариант4= метод учитывающий структуру компонент условий и значения, которые они

    принимают при выполнении тестовых примеров

    Критерии оценки:

    85% и более правильных ответов — оценка «отлично»

    от 70 до 85% правильных ответов — оценка «хорошо»

    от 50 до 70% правильных ответов — оценка «удовлетворительно»

    до 50% правильных ответов — оценка «неудовлетворительно»

    КОМПЛЕКТ МАТЕРИАЛОВ ДЛЯ ПРОМЕЖУТОЧНОЙ АТТЕСТАЦИИ

    Теоретические вопросы

    1.

    Модель. Моделирование. Информационная модель.

    2.

    Классификация методов моделирования.

    3.

    Концептуальное моделирование

    4.

    Имитационное моделирование

    5.

    Методы построения информационных систем. Метод “снизу-вверх”

    6.

    Методы построения информационных систем. Метод “сверху-вниз”

    7.

    Методы построения информационных систем. Принципы “дуализма” и

    многокомпонентности

    8.

    Методы построения информационных систем. Каскадная модель.

    9.

    Методы построения информационных систем. Спиральная модель.

    10. Автоматизированная система моделирования (АСМ)

    11. Объектно-ориентированный подход проектирования ИС.

    12. Цели и задачи моделирования ПО.

    15

    13. Модели при визуальном моделировании ПО

    14. Граф модели и диаграммы

    15. Ролевой состав коллектива разработчиков. Заказчик (заявитель).

    16. Ролевой состав коллектива разработчиков. Менеджер проекта

    17. Ролевой состав коллектива разработчиков. Менеджер программы

    18. Ролевой состав коллектива разработчиков. Разработчик

    19. Ролевой состав коллектива разработчиков. Специалист по тестированию

    20. Ролевой состав коллектива разработчиков. Специалист по контролю качества

    21. Ролевой состав коллектива разработчиков. Специалист по сертификации

    22. Ролевой состав коллектива разработчиков. Специалист по внедрению и

    сопровождению

    23. Ролевой состав коллектива разработчиков. Специалист по безопасности

    24. Ролевой состав коллектива разработчиков. Инструктор

    25. Ролевой состав коллектива разработчиков. Технический писатель

    26. Тестирование программного обеспечения

    27. Верификация программного обеспечения

    28. Валидация программной системы

    29. Модульное тестирование

    30. Интеграционное тестирование

    31. Системное тестирование

    32. Верификация сертифицируемого программного обеспечения

    33. Задачи и цели тестирования программного кода

    34. Методы тестирования. Черный ящик.

    35. Методы тестирования. Стеклянный (белый) ящик

    36. Методы тестирования. Тестирование моделей

    37. Методы тестирования. Анализ программного кода (инспекции)

    38. Тестовое окружение. Драйверы и заглушки

    39. Тестовое окружение. Тестовые классы

    40. Тестовое окружение. Генераторы сигналов (событийно-управляемый код)

    41. Типы тестовых примеров. Допустимые данные.

    42. Типы тестовых примеров. Граничные данные

    43. Типы тестовых примеров. Отсутствие данных

    44. Типы тестовых примеров. Повторный ввод данных

    45. Типы тестовых примеров. Неверные данные

    46. Типы тестовых примеров. Реинициализация системы

    47. Типы тестовых примеров. Устойчивость системы.

    48. Типы тестовых примеров. Нештатные состояния среды выполнения

    49. Типы тестовых примеров. Граничные условия.

    50. Тестовые примеры. Проверка робастности (выхода за границы диапазона)

    51. Тестовые примеры Классы эквивалентности.

    52. Тестовые примеры. Тестирование операций сравнения чисел

    53. Типовая структура тест-плана

    54. Оценка качества тестируемого кода – статистика выполнения тестов.

    55. Покрытие программного кода. Понятие покрытия

    56. Покрытие программного кода. Уровни покрытия. По строкам программного кода

    (Statement Coverage)

    57. Покрытие программного кода. Уровни покрытия. По веткам условных операторов

    (Decision Coverage)

    58. Покрытие программного кода. Уровни покрытия. Покрытие по условиям (Condition

    Coverage)

    59. Покрытие программного кода. Уровни покрытия. Покрытие по веткам/условиям

    (Condition/Decision Coverage)

    16

    60. Покрытие программного кода. Уровни покрытия. Покрытие по всем условиям (Multiple

    Condition Coverage)

    61. Покрытие программного кода. Анализ покрытия.

    62. Задачи и цели обеспечения повторяемости тестирования.

    63. Предусловия для выполнения теста, настройка тестового окружения, оптимизация

    последовательностей тестовых примеров.

    64. Зависимость между тестовыми примерами, настройки по умолчанию для тестовых

    примеров и их групп

    Перечень теоретических вопросов к дифференцированному зачету (экзамену)

    1. Цели, задачи, этапы и объекты моделирования и ревьюирования.

    2. Графические языки спецификаций.

    3. Основные элементы языка UML.

    4. Цели, корректность и направления анализа программных продуктов.

    5. Методики оценки программных продуктов.

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

    7. Механизмы и контроль внесения изменений в код.

    8. Обратное проектирование.

    9. Анализ потоков данных. Диаграмма потоков данных.

    10.Валидация кода на стороне сервера и разработчика.

    11. Совместимость и использование инструментов ревьюирования в различных системах

    контроля версий.

    12.Особенности ревьюирования в Linux. Настройки доступа.

    13.Типовые инструменты и методы анализа программных проектов.

    14.Измерительные методы оценки программ: назначение, условия применения.

    15.Корректность программ. Эталоны и методы проверки корректности.

    16.Метрики, направления применения метрик. Метрики сложности. Метрики стилистики.

    17.Исследование программного кода на предмет ошибок и отклонения от алгоритма.

    18.Основные понятия управления проектами. Специфика управления проектами в сфере IT.

    19.Методологии анализа и управления информационных систем.

    20.Общая характеристика проектов внедрения ИТ-решений.

    21.Определение ЖЦ продукта ИТ-проекта. Обзор моделей жизненного цикла. Обоснование и

    приоретизация проекта.

    22.Устав проекта.

    23.Границы проекта.

    24.Реестр заинтересованных лиц.

    25.Матрица ожиданий и требований. Балансировка требований.

    26.Содержание проекта.

    27.Обзор ПО для управления проектами.

    28.Иерархическая структура работ. Методы формирования списка работ.

    29.Планирование ресурсов. Виды ресурсов.

    30.Методы определения продолжительности проекта.

    31.Планирование стоимости проекта. Методы расчета стоимости проекта.

    32.Расписание проекта. Сетевой анализ расписания проекта.

    33.Распределение ответственности. Матрица ответственности. Закрепление функций и

    полномочий в проекте.

    34.Календарно-ресурсный план.

    35.План обеспечения проекта персоналом.

    36.Планирование коммуникаций в проекте.

    37.План управления качеством.

    38.Риски, виды рисков проекта. Реестр рисков.

    39.Качественный анализ рисков.

    17

    40.Количественный анализ рисков.

    41.Планирование реагирования на риски.

    42.Управление исполнением проекта. Отслеживание выполнения работ. Управление

    требованиями.

    43.Выявление потребностей пользователей. Мотивирование и развитие команды. 44.Закрытие

    проекта.

    Перечень практических заданий к дифференцированному зачету (экзамену)

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

    2. Определить заинтересованных лиц проекта.

    3. Составить матрицу ответственности.

    4. Определить список необходимых для выполнения проекта трудовых и материальных

    ресурсов. Определить рабочее время для сотрудников, работающих по особому графику,

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

    определить типы задач.

    5. В рамках предполагаемого времени проведения проекта определить календарь рабочего

    времени, учитывая праздничные дни РФ, сокращенное рабочее время предпраздничных дней

    и переносы рабочих дней.

    6. Определить состав работ: фазы, задачи, вехи; приблизительные длительности задач, типы

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

    известна, но лишь приблизительно и впоследствии может быть изменена. Определить

    приблизительные сроки выполнения всего проекта.

    7. Определить ставки трудовых ресурсов и порядок оплаты работ, определить стоимость

    материальных ресурсов, учесть штрафы за выполнение работ не в срок. Учесть оклады

    штатных сотрудников и премии по окончании работ. Определить итоговую стоимость

    проекта. Ввести информацию о бюджете, сравнить с оценочными данными.

    8. Выровнять загрузку тех сотрудников, кто не согласился работать сверхурочно. Учесть в

    стоимости проекта сверхурочную занятость сотрудников. Изменить план проекта в связи с

    переносами сроков окончания проекта. Оптимизировать стоимость проекта в связи с

    уменьшением бюджета.

    9. Рассчитать длительность и вероятность завершения в заданный срок проекта, заданного

    сетевым графиком и имеющего известные параметры работ.

    10.Управление стоимостью проекта. NASA заказало самарскому СНТК Кузнецова

    изготовление двигателей для ракеты-носителя «Атлантис». Оценить значения показателей CV,

    SV, CPI, SPI на момент окончания проекта по методу освоенного объема. Известны плановые

    и фактические показатели проекта.

    11. Рассчитать показатели NPV, PI и РР проекта длительностью 6 лет, если известны ставка

    дисконтирования, инвестиции, расходы и доходы. Рассчитать длительность проекта.

    12.Определить вероятность завершения проекта в заданный срок.

    13.Исследовать влияние неопределенности длительности работ на вероятность завершения

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

    моделирования нагрузок в силовых конструкциях самолетов. Известны параметры работ,

    сетевой график и желаемый срок завершения проекта.

    Деловая игра

    по междисциплинарному курсу 03.01 «Моделирование и анализ программного

    обеспечения»

    Тема: «Ревьюирование предложенного программного кода на соответствие требованиям

    технического задания на проект».

    Концепция игры. Участники игры получают от преподавателя персональный программный

    код и техническое задание на проект. Каждый обучающийся должен самостоятельно

    построить модель программного кода, определить его характеристики, провести анализ

    18

    программного кода и его соответствие техническому заданию, а также предложить

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

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

    по максимальному объему выполненных анализа и оптимизации.

    Роли:

    • тестировщик — производит анализ и оптимизацию программного кода;

    • эксперты — оценивают анализ и проведенную оптимизацию.

    Ожидаемый результат:

    понимание правильного написания программного кода для

    дальнейшего тестирования и обнаружения несоответствия техническому заданию.

    Критерии оценки:

    Оценка 5 «отлично»: участник справился с ролью без недочетов и ошибок.

    Оценка 4 «хорошо»: участник справился с ролью с некоторыми недочетами, не повлиявшими

    на конечный результат.

    Оценка 3 «удовлетворительно»: участник справился с ролью с ошибками, повлиявшими на

    конечный результат.

    Оценка 2 «неудовлетворительно»: участник не справился с ролью.

    Деловая игра

    по междисциплинарному курсу 03.02 «Управление проектами»

    Тема: «Зависимость финансового результата от качества сделанной работы».

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

    предыдущих

    практических

    занятий.

    Каждый

    обучающийся

    должен

    самостоятельно

    определить методы анализа и оптимизации проекта и реализовать их на практике.

    Результаты выполнения представляются разработчиком проекта и оцениваются остальными

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

    максимальному объему сделанных инвестиций.

    Роли:

    • разработчик проекта — производит анализ и оптимизацию проекта;

    • инвестор — оценивает проект и делает инвестиции, в случае положительной оценки.

    Ожидаемый результат:

    понимание зависимости финансового результата от качества

    сделанной работы.

    Критерии оценки:

    Оценка 5 «отлично» — более 75% инвесторов сделали вложения в проект.

    Оценка 4 «хорошо» — от 50 до 75% инвесторов сделали вложения в проект.

    Оценка 3 «удовлетворительно» — от 25 до 50% инвесторов сделали вложения в проект.

    Оценка 2 «неудовлетворительно» — менее 25% инвесторов сделали вложения в проект.

    Деловая игра

    по междисциплинарному курсу 03.02 «Управление проектами»

    Тема:

    «Анализ рисков. Необходимость учета эстетического фактора при применении

    информационной».

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

    предыдущих

    практических

    занятий.

    Каждый

    обучающийся

    должен

    самостоятельно

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

    реакции на риски.

    Результаты выполнения представляются разработчиком проекта и оцениваются остальными

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

    максимальному объему сделанных инвестиций.

    Роли:

    19

    • разработчик проекта — производит анализ рисков и оптимизацию проекта, с учетом

    проведенного анализа;

    • инвестор — оценивает проект и делает инвестиции, в случае положительной оценки.

    Ожидаемый результат:

    понимание зависимости финансового результата от качества

    сделанной работы.

    Критерии оценки:

    Оценка 5 «отлично» — более 75% инвесторов сделали вложения в проект.

    Оценка 4 «хорошо» — от 50 до 75% инвесторов сделали вложения в проект.

    Оценка 3 «удовлетворительно» — от 25 до 50% инвесторов сделали вложения в проект.

    Оценка 2 «неудовлетворительно» — менее 25% инвесторов сделали вложения в проект.

    Деловая игра

    по междисциплинарному курсу 03.02 «Управление проектами»

    Тема: «Создание физического дизайна. Зависимость результата от командных условий».

    Концепция игры.

    Участники игры делятся на две проектные команды и распределяют между собой роли (см.

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

    отведенное время должны справиться со своими ролями, подготовить общую картину

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

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

    Роли:

    менеджер

    решения:

    управление

    ожиданиями

    пользователей

    и

    создание

    плана

    взаимодействия, подготовка к развертыванию решения;

    менеджер

    программы:

    управление

    процессом

    физического

    дизайна

    и

    создание

    функциональной спецификации;

    • разработчик: представление проектных моделей, планов, календарных графиков и смет

    разработки;

    • специалист по удобству использования: оценка физического дизайна на предмет

    удовлетворения требований пользователей и разработка плана справочной системы;

    • тестировщик: оценка и проверка функционального наполнения и целостности физического

    дизайна на основании СИС;

    • менеджер по выпуску: оценка влияния инфраструктуры на физический дизайн. Ожидаемый

    результат: понимание важности подбора команды и ответственного отношения к делу каждого

    из ее участников.

    Критерии оценки:

    Оценка 5 «отлично»: участник справился с ролью без недочетов и ошибок.

    Оценка 4 «хорошо»: участник справился с ролью с некоторыми недочетами, не повлиявшими

    на конечный результат.

    Оценка 3 «удовлетворительно»: участник справился с ролью с ошибками, повлиявшими на

    конечный результат.

    Оценка 2 «неудовлетворительно»: участник не справился с ролью.

    20

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

    Программная ошибка: что это и почему возникает

    Программная ошибка — это дефект в коде. Из-за него программа сбоит или выдает неверные результаты. Некоторые ошибки серьезные — например, блокируют логин и пароль, из-за чего пользователь не может попасть в личный кабинет. А другие незаметны. Некоторое время программа работает как будто бы исправно — и только потом начинает глючить.

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

    Научитесь находить ошибки в приложениях и на сайтах до того, как ими начнут пользоваться клиенты. Для этого освойте профессию «Инженер по тестированию». Изучать язык программирования необязательно. Тестировщик работает с готовыми сайтами, приложениями, сервисами, а не с кодом. В программе от Skypro: четыре проекта для портфолио, практика с обратной связью, все основные инструменты тестировщика.

    Ошибки часто называют багами, но подразумевают под ними разное, например:

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

    Исключения. Это не ошибки, а особые ситуации, которые нужно обработать.

    Синтаксические ошибки. Это ошибка в программе, связанная с написанием кода. Пример: программист забыл поставить точку или неверно написал название оператора. Если не исправить, код программы не запустится, а останется просто текстом.

    Классификация багов

    У багов есть два атрибута — серьезности (Severity) и приоритета (Priority). Серьезность касается технической стороны, а приоритет — организационной.

    🚨 По серьезности. Атрибут показывает, как сильно ошибка влияет на общую функциональность программы. Чем выше значение атрибута, тем хуже.

    По серьезности баги классифицируют так:

    • Blocker — блокирующий баг. Программа запускается, но спустя время баг останавливает ее выполнение. Чтобы снова пользоваться программой, блокирующую ошибку в коде устраняют.
    • Critical — критический баг. Нарушает функциональность программы. Появляется в разных частях кода, из-за этого основные функции не выполняются.
    • Major — существенный баг. Не нарушает, но затрудняет работу основного функционала программы либо не дает функциям выполняться так, как задумано.
    • Minor — незначительный баг. Слабо влияет на функционал программы, но может нарушать работу некоторых дополнительных функций.
    • Trivial — тривиальный баг. На работу программы не влияет, но ухудшает общее впечатление. Например, на экране появляются посторонние символы или всё рябит.

    🚦 По приоритету. Атрибут показывает, как быстро баг необходимо исправить, пока он не нанес программе приличный ущерб. Бывает таким:

    • Top — наивысший. Такой баг — суперсерьезный, потому что может обвалить всю программу. Его устраняют в первую очередь.
    • High — высокий. Может затруднить работу программы или ее функций, устраняют как можно скорее.
    • Normal — обычный. Баг программу не ломает, просто где-то что-то будет работать не совсем верно. Устраняют в штатном порядке.
    • Low — низкий. Баг не влияет на программу. Исправляют, только если у команды есть на это время.

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

    🧨 Логические. Приводят к тому, что программа зависает, работает не так, как надо, или выдает неожиданные результаты — например, не записывает файл, а стирает.
    Логические ошибки коварны: их трудно обнаружить. Программа выглядит так, будто в ней всё правильно, но при этом работает некорректно. Чтобы победить логические ошибки, специалист должен хорошо ориентироваться в коде программы.

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

    🧨 Взаимодействия. Это ошибка в участке кода, который отвечает за взаимодействие с аппаратным или программным окружением. Такая ошибка возникает, например, если неправильно использовать веб-протоколы. Исправляется элементарно: разработчик переписывает нужный кусок кода.

    🧨 Компиляционные. Любая программа — это текст. Чтобы он заработал как программа, используют компилятор. Он преобразует программный код в машинный, но одновременно может вызывать ошибки.

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

    🧨 Ошибки среды выполнения. Возникают, когда программа скомпилирована и уже выглядит как файл — жми и работай. Юзер запускает файл, а программа тормозит и виснет. Причина — нехватка ресурсов, например памяти или буфера.

    Такой баг — ошибка разработчика. Он не предвидел реальные условия развертывания программы. Теперь ему надо вернуться в исходный код и поправить фрагмент.

    🧨 Арифметические. Бывает, в коде есть числовые переменные и математические формулы. Если где-то проблема — не указаны константы или округление сработало не так, возникает баг. Надо лезть в код и проверять математику.

    Инженер-тестировщик: новая работа через 9 месяцев

    Получится, даже если у вас нет опыта в IT

    Узнать больше

    Что такое исключения в программах

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

    Как это происходит:

    1. Когда программист кодит, то продумывает, в какой части программы может вылезти ошибка.
    2. В этой части пишет специальный фрагмент, который предупредит компьютер, что ошибка — вполне ожидаемое явление и резко обрывать программу не нужно.
    3. Когда юзер запустит программу и появится ошибка, компьютер увидит заранее подготовленное предупреждение программиста. Продолжит выполнять алгоритм так, словно никакого бага и не было.

    Исключения бывают программными и аппаратными:

    • Аппаратные создает процессор. К ним относят деление на ноль, выход за границы массива, обращение к невыделенной памяти.
    • Программные создает операционка и приложения. Возникают, когда программа их инициирует: аномальная ситуация возникла — программа создала исключение.

    Как контролировать баги в программе

    🔧 Следите за компилятором. Когда компилятор преобразует текст программы в машинный код, то подсвечивает в нём сомнительные участки, которые способны вызывать баги. Некоторые предупреждения не обозначают баг как таковой, а только говорят: «Тут что-то подозрительное». Всё подозрительное надо изучать и прорабатывать, чтобы не было проблемы в будущем.

    🔧 Используйте отладчик. Это программа, которая без участия айтишника проверяет, исправно ли работает алгоритм. В случае чего сообщает об ошибках. Например, отладчик используют для построчного выполнения программы. Вместе с тем проверяют значения переменных: фактические сравнивают с ожидаемыми. Если что-то не сходится, ищут баги и исправляют.

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

    Ключевое: что такое ошибки в программировании

    • Ошибка в программировании — это дефект кода, баг, который может вызывать в программе сбои и неожиданное поведение.
    • По серьезности баги делятся на блокирующие, критические, существенные, незначительные, тривиальные. По приоритету — на наивысший, высокий, обычный, низкий.
    • Ошибки в коде могут быть разными, например связанные с логикой программы. Или с математическими вычислениями — логические. Еще бывают синтаксические, ошибки взаимодействия, компиляционные и ошибки среды выполнения.
    • Некоторые ошибки помогают ловить обработчики исключений.
    • Чтобы находить ошибки в коде, тестировщики используют компиляторы, отладчики и пишут юнит-тесты.

  • Ошибки при программировании ардуино
  • Ошибки при проверке кассовой дисциплины
  • Ошибки при проверке h2testw
  • Ошибки при проведении экг
  • Ошибки при проведении фокус группы