В языке C нет встроенной поддержки исключений, обработка ошибок реализуется следующими способами:
Возврат кода ошибки (обычно через значение функции) Пример:
int read_file(const char *name) { FILE *f = fopen(name, "r"); if (!f) return -1; // Ошибка // ... fclose(f); return 0; // Успех }
Использование глобальной переменной errno
При ошибке стандартные функции часто устанавливают глобальную переменную errno.
Пример:
FILE *fp = fopen("nofile", "r"); if (!fp) { perror("Ошибка файла"); // errno определяет причину }
setjmp/longjmp — механизм броска "исключения" (контекст выполнения сохраняется и восстанавливается) Применяется для сложных случаев (например, межуровневое аварийное завершение). Используется редко, т.к. усложняет код и может привести к утечкам ресурсов.
#include <setjmp.h> jmp_buf buf; if (setjmp(buf) == 0) { // ... longjmp(buf, 1); // Переходит к if (setjmp(...)) } else { // Обработка ошибки }
Что выведет следующий код?
#include <stdio.h> #include <setjmp.h> jmp_buf buf; void func() { longjmp(buf, 5); } int main() { int val = setjmp(buf); printf("val=%d ", val); if (val == 0) func(); return 0; }
Ответ:
setjmp возвращает 0 и вызывается функция func.longjmp(buf, 5), выполнение переходит обратно к setjmp, который теперь возвращает 5.val=0
val=5
История В одной библиотеке был реализован ручной механизм очистки ресурсов при ошибке только по коду возврата. Но программист забыл обработать ошибку при возврате не из основного потока, а из callback (через setjmp/longjmp), в результате были утечки памяти и блокировки файлов.
История В сетевом приложении пропустили проверку
errnoпосле операции socket. Из-за этого возникала ложная диагностика: переменнаяerrnoсохранила старое некорректное значение от предыдущей функции.
История Команда реализовала сложную вложенность с множеством точек возврата кода ошибки, но не придерживались конвенции о значениях возврата (ноль/отрицательные значения). Некоторые ошибки интерпретировались как успешные, приводя к непредсказуемым отказам сервиса.