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

Как:

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

Возвращение специальных значений

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

#include <stdio.h>

int inverse(int number, double *result) {
    if (number == 0) {
        return -1; // Ошибка
    } else {
        *result = 1.0 / number;
        return 0; // Успех
    }
}

int main() {
    double result;
    if (inverse(0, &result) < 0) {
        printf("Ошибка: Деление на ноль.\n");
    } else {
        printf("Обратное значение: %f\n", result);
    }
    
    return 0;
}

Вывод:

Ошибка: Деление на ноль.

Проверка errno

Для библиотечных функций, особенно тех, которые взаимодействуют с системой или ОС (например, I/O файлов), errno устанавливается, когда происходит ошибка. Для его использования подключите errno.h и проверяйте errno после предполагаемого сбоя:

#include <stdio.h>
#include <errno.h>
#include <string.h>

int main() {
    FILE *file = fopen("nonexistent.txt", "r");
    if (file == NULL) {
        printf("Ошибка при открытии файла: %s\n", strerror(errno));
    }
    
    return 0;
}

Вывод:

Ошибка при открытии файла: Нет такого файла или каталога

Погружение

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

Хотя этот подход хорошо сочетается с целями дизайна C, он также может привести к громоздкому коду проверки ошибок и потенциалу пропуска проверок ошибок, что современные языки решают с помощью структурированных механизмов обработки исключений. Например, исключения в языках, таких как Java или C#, позволяют централизованно обрабатывать ошибки, делая код чище и управление ошибками более простым. Однако, исключения вносят свои накладные расходы и сложности, которые могут быть неидеальными для системного программирования, где C блестит.

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