Операционная система UNIX. Руководство программиста

     

Обработка ошибок


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

В случае неудачного завершения почти всегда системные вызовы ОС UNIX возвращают вызвавшей программе значение -1. (Если Вы просмотрите описание системных вызовов в разделе 2, Вы увидите, что имеется все же несколько вызовов, для которых возвращаемое значение не определено, но это исключения.) При неудачном завершении, кроме возврата значения -1, системные вызовы помещают код ошибки во внешнюю переменную errno. Чтобы переменная errno была доступна Вашей программе, необходимо включить в программу оператор

#include <errno.h>

При успешном завершении системного вызова значение переменной errno не изменяется, поэтому оно имеет смысл только в случае, когда какой-либо системный вызов вернул -1. Список кодов ошибок приведен в Справочнике программиста в статье intro(2).

Для того, чтобы по коду ошибки, помещенному в errno, вывести в стандартный протокол сообщение об ошибке, можно воспользоваться функцией perror(3C).


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

Такие правила, как предыдущее, универсальны, но ими трудно управлять. Правила, подобные

stat : error ';'

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

Еще одна форма error-правила возникает в интерактивных приложениях, где может потребоваться разрешить после ошибки перенабрать строку. Пример

input : error '\n' { printf ("Перенаберите последнюю строку: "); } input { $$ = $4; } ;

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

yyerrok ;

возвращает алгоритм в нормальное состояние. Последний пример можно переписать в виде

input : error '\n' { yyerrok; printf ("Перенаберите последнюю строку: "); } input { $$ = $4; } ;

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

yyclearin ;

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

stat : error { resynch (); yyerrok; yyclearin; } ;

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




Содержание раздела