Обработка ошибок в языке C всегда была задачей разработчика. В стандартной библиотеке нет исключений, ошибки возвращаются через коды возврата или глобальную переменную errno (начало использования — UNIX 1970-х годов, далее POSIX и ANSI C). Такие механизмы используются и сегодня для управления потоком выполнения при нештатных ситуациях.
Ошибки при работе со стандартными функциями (файловые операции, выделение памяти, строковые функции) могут быть незаметны без особого контроля. Неправильная обработка — игнорирование кода возврата, неправильная интерпретация errno, отсутствие очистки ресурсов — приводит к неверной работе программы, падениям и уязвимостям.
Корректная обработка ошибок требует обязательного анализа значений, возвращаемых функциями, использования errno только сразу после сбоя, и информативного вывода ошибок. Коды возврата предпочтительны для внутренних функций — позволяют делать обработку без глобальных побочных эффектов. errno чаще применяется с системными вызовами и функциями стандартной библиотеки. После каждой потенциально опасной операции анализируется возврат, а глобальное состояние (errno) не должно быть затёрто промежуточными вызовами.
Пример кода:
#include <stdio.h> #include <errno.h> #include <string.h> FILE *open_file(const char *filename) { errno = 0; FILE *f = fopen(filename, "r"); if (!f) { fprintf(stderr, "Ошибка: %s ", strerror(errno)); } return f; }
Ключевые особенности:
Можно ли использовать errno для пользовательских функций, если хочется передавать ошибки наверх?
Нет, errno предназначена только для стандартных библиотечных и системных вызовов. Она глобальна, может быть перезаписана в любой точке и не подходит для своих функций.
Обязательно ли устанавливать errno перед каждым вызовом функции?
Нет, но рекомендуется сбрасывать errno (например, к нулю) перед вызовами, если планируется анализ изменений. Не каждая функция изменяет errno при успехе, а только при ошибке.
errno = 0; ... вызов опасной функции ...
Можно ли доверять errno после любой функции?
Только для тех функций, которые согласно стандарту явно устанавливают её при неудаче. Многие функции стандартной библиотеки не трогают errno при успехе. Документация — ваш друг.
Открытие файла без проверки результата, ошибки не анализируются, программа работает неверно при отсутствии файла:
Плюсы:
Минусы:
После каждой критичной функции результат проверяется, в случае ошибки выводится подробное сообщение с strerror(errno), исполнение корректно завершается:
Плюсы:
Минусы: