ПрограммированиеC разработчик

Как реализуется обработка ошибок в языке C? В чем различия между setjmp/longjmp, errno и возвратом кода ошибки? Когда использовать тот или иной подход?

Проходите собеседования с ИИ помощником Hintsage

Ответ

В языке C нет встроенной поддержки исключений, обработка ошибок реализуется следующими способами:

  1. Возврат кода ошибки (обычно через значение функции) Пример:

    int read_file(const char *name) { FILE *f = fopen(name, "r"); if (!f) return -1; // Ошибка // ... fclose(f); return 0; // Успех }
  2. Использование глобальной переменной errno При ошибке стандартные функции часто устанавливают глобальную переменную errno. Пример:

    FILE *fp = fopen("nofile", "r"); if (!fp) { perror("Ошибка файла"); // errno определяет причину }
  3. setjmp/longjmp — механизм броска "исключения" (контекст выполнения сохраняется и восстанавливается) Применяется для сложных случаев (например, межуровневое аварийное завершение). Используется редко, т.к. усложняет код и может привести к утечкам ресурсов.

    #include <setjmp.h> jmp_buf buf; if (setjmp(buf) == 0) { // ... longjmp(buf, 1); // Переходит к if (setjmp(...)) } else { // Обработка ошибки }
  • Возврат кода ошибки — основной способ, простой и безопасный.
  • errno — удобно для библиотечных функций.
  • setjmp/longjmp — для специфических случаев (сложные библиотеки или интерпретаторы).

Вопрос с подвохом

Что выведет следующий код?

#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 сохранила старое некорректное значение от предыдущей функции.


История Команда реализовала сложную вложенность с множеством точек возврата кода ошибки, но не придерживались конвенции о значениях возврата (ноль/отрицательные значения). Некоторые ошибки интерпретировались как успешные, приводя к непредсказуемым отказам сервиса.