Gestion d'erreurs en C

Exemples d'erreurs

Fichiers

FILE *file;
file = fopen("tilemap.txt", "r");

if (file == NULL) {
    //quelle erreur
    return -1; // ou exit(-1)
}

Avec un fichier on peut savoir un stream est dans un état d'erreur avec :

int ferror( FILE * stream );

qui renvoie en fait un booléen.

Problème de réseau

Cf. cours au 1er semestre

Problème de chargement d'une librairie dynamique

Numéro d'erreurs

Quand il y a une erreur dans une fonction, on a un accès un numéro d'erreurs avec la variable globale errno disponible avec #include <errno.h>.

  • errno == 0 : pas de soucis ✅
  • errno != 0 : il y a eu un problème ❌

Message d'erreur

Aussi, la fonction

char * strerror ( int errCode );

affiche le message d'erreur textuel compréhensible par un humain correspondant à l'erreur. La fonction strerror est disponible via #include <string.h>. Typiquement, on appelle :

puts(strerror(errno));

⚠ On ne libère pas la mémoire de la chaîne de caractères d'erreur : free(strerror(errno));

  • D'après vous pourquoi on ne libère pas la mémoire d'une chaîne de caractères renvoyés par strerror ?

Parce que ce sont des chaînes de caractères dans le segment de données en lecture seule (.rodata, read-only data).

Informer l'utilisateur sur la sortie d'erreur

void perror( const char * prefix );

Elle affiche un message d'erreur sur stderr. Comme stdin et stdout qui sont respectivement les flux standards pour l'entrée et la sortie, stderr est le flux standard pour les erreurs. stderr est généralement redirigé vers la console, comme stdout.

Utilisation mot-clé goto

https://github.com/openbsd/src/blob/master/sys/dev/ata/wd.c

setjmp et longjmp

Devoir gérer les erreurs en cas d'imbrication de fonctions est fastidieux.

En Java, Python, C++, et autres,on a des exceptions pour gérer les erreurs. En C, non, mais il y a setjmp et longjmp de <setjmp.h>.

  1. Tu poses un jalon pour y revenir plus tard
  2. Tu exécutes des choses (chargement d'un gros fichier par exemple)
  3. Tu tombes sur une erreur en croisant un ours
  4. Tu reviens au jalon et tu affiches un message d'erreur, tu libères la mémoire et tu reviens dans une situation stable
int setjmp(jmp_buf env);

setjmp pose un jalon 🚩. Elle écrit dans env les données relatives au contexte de la pile d'appel actuel. Cette fonction renvoie 0... sauf que...

void longjmp(jmp_buf env, int value);

longjmp renvoie au jalon avec le contexte de l'époque. On renvoie dans le code où il y avait l'appel à setjmp qui maintenant renvoie value (qui doit être différente de 0).

#include <stdio.h>
#include <setjmp.h>

static jmp_buf jmpBuffer;


void b() {
    ...
    if(error) {
        longjmp(jmpBuffer, 1);
    }
}


void a() {
    b();
}

void bigComputation() {
    a();
}

int main() {
    if (setjmp(jmpBuffer) == 0)
        bigComputation();
    else
        printf("désolé il y a eu une erreur\n"); 

    return EXIT_SUCCESS;
}



Exemple d'utilisation : https://sourceforge.net/p/libpng/code/ci/master/tree/example.c#l322