logo
Языки программирования

11.1. Требования обработки исключительных ситуаций

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

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

Какие свойства делают средства обработки исключений хорошими?

• В случае отсутствия исключения издержки должны быть очень неболь­шими.

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

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

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

Ada

if Ptr.Next= null then . . . else . . .

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

В качестве элементарной обработки исключений в некоторых языках пользователю дана возможность определять блок кода, который будет вы­полнен перед завершением программы. Это полезно для наведения порядка (закрытия файлов и т.д.) перед выходом из программы. В языке С средство setjmp/longjmp позволяет пользователю задать дополнительные точки внут­ри программы, в которые обработчик исключений может возвращаться. Этого типа обработки исключений достаточно, чтобы гарантировать, что программа «изящно» завершится или перезапустится, но он недостаточно гибок для детализированной обработки исключений.

Обратите внимание, что, согласно нашему определению исключения как непредвиденной ошибки на этапе выполнения, в языке С «исключений» меньше, чем в таком языке, как Ada. Во-первых, такие ошибки, как выход за границы массива, не определены в языке С; они просто являются ошибками программиста, которые не могут быть «обработаны». Во-вторых, поскольку в С нет гибкого средства обработки исключений, каждая возможность языка, которая запрашивается через подпрограмму, возвращает код, указывающий, был запрос успешным или нет. Таким образом, в языке Ada распределение па­мяти с помощью new может вызвать исключительную ситуацию, если нет до­статочного объема памяти, тогда как в С malloc возвращает код, который дол­жен быть проверен явно. Выводы для стиля программирования следующие: в Ada можно использовать new обычным порядком, а обработку исключений проектировать независимо, в то время как в С полезно написать подпрограм-му-оболочку для malloc так, чтобы реакцию на исключительные ситуации можно было разработать и запрограммировать централизованно, вместо того чтобы разрешать каждому члену группы тестировать (или забывать тестиро­вать) нехватку памяти:

void* get_memory(int n)

C

{

void* p = malloc(n);

if (p == 0) /* Выделение памяти потерпело неудачу */

/* Сделайте что-нибудь или корректно

завершите работу */

return р;

}

Yandex.RTB R-A-252273-3
Yandex.RTB R-A-252273-4