Несмотря на стремление сэкономить на наборе текста путем исключения подобных проверок (особенно после просмотра примеров программ, написанных под UNIX и Linux, где коды завершения не проверяются), это «экономия на спичках». Из-за отсутствия проверки кода, возвращенного системным вызовом или вызовом библиотечной функции, которая «в принципе не должна дать сбой», можно впустую потратить многие часы на отладку.
Есть несколько системных вызовов, никогда не дающих сбоев. Например, getpid() всегда успешно возвращает идентификатор процесса, а _exit() всегда прекращает процесс. Проверять значения, возвращаемые такими системными вызовами, нет никакого смысла.
Возможные возвращаемые вызовом значения документируются на странице руководства по каждому системному вызову, и там показывается значение (или значения), свидетельствующее об ошибке. Обычно ошибка выявляется возвращением значения –1. Следовательно, системный вызов может быть проверен с помощью такого кода:
fd = open(pathname, flags, mode); /* Системный вызов для открытия файла */
if (fd == -1) {
/* Код для обработки ошибки */
}
…
if (close(fd) == -1) {
/* Код для обработки ошибки */
}
При неудачном завершении системного вызова для глобальной целочисленной переменной errno устанавливается положительное значение, позволяющее идентифицировать конкретную ошибку. Объявление errno, а также набора констант для различных номеров ошибок предоставляется за счет включения заголовочного файла
cnt = read(fd, buf, numbytes);
if (cnt == -1) {
if (errno == EINTR)
fprintf(stderr, "read was interrupted by a signal\n");
// чтение было прервано сигналом
else {
/* Произошла какая-то другая ошибка */
}
}
Успешно завершенные системные вызовы и вызовы библиотечных функций никогда не сбрасывают errno в 0, следовательно, эта переменная может иметь ненулевое значение из-за ошибки предыдущего вызова. Более того, SUSv3 разрешает успешно завершающей свою работу функции устанавливать для errno ненулевое значение (хотя это делают всего несколько функций). Поэтому при проверке на ошибку нужно всегда сперва проверить, не вернула ли функция значение, свидетельствующее о возникновении ошибки, и только потом исследовать errno для определения причины ошибки.
Некоторые системные вызовы (например, getpriority()) могут вполне законно возвращать при успешном завершении значение –1. Чтобы определить, не возникла ли при таких вызовах ошибка, перед самим вызовом нужно установить errno в 0 и проверить ее значение после вызова. Если вызов возвращает –1, а errno имеет ненулевое значение, значит, произошла ошибка. (Это же правило применимо к некоторым библиотечным функциям.)
Общая линия поведения после неудачного системного вызова заключается в выводе сообщения об ошибке на основе значения переменной errno. Для этой цели предоставляются библиотечные функции perror() и strerror().
Функция perror() выводит строку, указываемую с помощью аргумента msg. За строкой следует сообщение, соответствующее текущему значению переменной errno.
#include
void perror(const char *
Простой способ обработки ошибок из системных вызовов будет выглядеть следующим образом:
fd = open(pathname, flags, mode);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
Функция strerror() возвращает строку описания ошибки, соответствующую номеру ошибки, который задан в ее аргументе errnum.
#include
char *strerror(int
Возвращает указатель на строку с описанием ошибки, соответствующую значению errnum
Строка, возвращенная strerror(), может быть размещена статически, что означает, что она может быть переписана последующими вызовами strerror().
Если в errnum указан нераспознаваемый номер ошибки, то strerror() возвращает строку вида Unknown error nnn (неизвестная ошибка с таким-то номером). В некоторых других реализациях strerror() в таких случаях возвращает значение NULL.
Поскольку функции perror() и strerror() чувствительны к настройкам локали (см. раздел 10.4), описания ошибок выводятся на языке локали.
Различные библиотечные функции для обозначения ошибок возвращают разные типы данных и разные значения. (По каждой функции нужно обращаться к странице руководства.) В рамках этого раздела библиотечные функции могут быть разбиты на несколько категорий.