C:
Gerenciando erros

Como fazer:

C não possui suporte embutido para exceções como algumas outras linguagens. Em vez disso, ele depende de algumas estratégias convencionais de tratamento de erros, como retornar valores especiais de funções e configurar variáveis globais como errno.

Retornando Valores Especiais

As funções podem indicar erros retornando um valor específico que é improvável de ser um resultado válido. Aqui está um exemplo com inteiros:

#include <stdio.h>

int inverse(int number, double *result) {
    if (number == 0) {
        return -1; // Caso de erro
    } else {
        *result = 1.0 / number;
        return 0; // Sucesso
    }
}

int main() {
    double result;
    if (inverse(0, &result) < 0) {
        printf("Error: Divisão por zero.\n");
    } else {
        printf("O inverso é: %f\n", result);
    }
    
    return 0;
}

Saída:

Error: Divisão por zero.

Verificando errno

Para funções de biblioteca, especialmente aquelas que interagem com o sistema ou SO (como E/S de arquivo), errno é definido quando ocorre um erro. Para usá-lo, inclua errno.h e verifique errno após uma falha suspeita:

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

int main() {
    FILE *file = fopen("nonexistent.txt", "r");
    if (file == NULL) {
        printf("Erro ao abrir arquivo: %s\n", strerror(errno));
    }
    
    return 0;
}

Saída:

Erro ao abrir arquivo: No such file or directory

Aprofundamento

Historicamente, o design minimalista da linguagem de programação C excluiu um mecanismo de tratamento de exceções embutido, refletindo suas origens de programação de sistemas de baixo nível, onde o desempenho máximo e o controle próximo ao hardware são críticos. Em vez disso, o C adota uma abordagem de tratamento de erros mais manual que se encaixa em sua filosofia de dar aos programadores o máximo controle possível, mesmo à custa da conveniência.

Embora esta abordagem esteja bem alinhada com os objetivos de design do C, também pode levar a um código de verificação de erro verboso e ao potencial para verificações de erro perdidas, questões que linguagens modernas abordam com mecanismos estruturados de tratamento de exceções. Por exemplo, exceções em linguagens como Java ou C# permitem o processamento centralizado de erros, tornando o código mais limpo e a gestão de erros mais direta. No entanto, exceções introduzem seu próprio custo e complexidade, que podem não ser ideais para programação em nível de sistema onde o C brilha.

Apesar de sua crueza, esse tratamento manual de erros em C informou o design da gestão de erros em muitas outras linguagens, oferecendo um modelo onde a explicitação das condições de erro pode levar a um código mais previsível e depurável. Para sistemas críticos, onde falhas devem ser gerenciadas de forma graciosa, o paradigma de tratamento de erros do C - combinado com as melhores práticas modernas como bibliotecas e convenções de gerenciamento de erros - garante robustez e confiabilidade.