La gestion des erreurs en C

Formation

En Ligne

Prix sur demande

Appeler le centre

Avez-vous besoin d'un coach de formation?

Il vous aidera à comparer différents cours et à trouver la solution la plus abordable.

Description

  • Typologie

    Formation

  • Méthodologie

    En ligne

Grâce à cette formation vous pourrez acquérir les connaissances nécessaires qui vous permettrons d’ajouter des compétences à votre profil et obtenir de solides aptitude qui vous offriront de nombreuses opportunités professionnelles.

Questions / Réponses

Ajoutez votre question

Nos conseillers et autres utilisateurs pourront vous répondre

À qui souhaitez-vous addresser votre question?

Saisissez vos coordonnées pour recevoir une réponse

Nous ne publierons que votre nom et votre question

Les Avis

Les matières

  • C++
  • Gestion

Le programme

Introduction du cours

Introduction

Tout au long de votre apprentissage progressif du langage C, vous avez pu remarquer que certaines fonctions échouaient souvent et pour des raisons variées. Jusqu'à présent, vous vous êtes peut-être contentés de tester le retour de la fonction et d'afficher un simple message d'erreur en conséquence. Bien que ce soit une très bonne habitude, cela ne suffit souvent pas. L'objet de ce tutoriel sera de gérer des diagnostics plus précis quant aux erreurs détectées, et ainsi vous permettre d'agir en conséquence.

A l'origine, le langage C était conçu pour la programmation système. Pour simplifier, cette dernière consiste au développement d'applications plus bas niveau qui utilisent des primitives proches du noyau de la machine. Vous pouvez donc imaginer que les erreurs détectées y sont souvent complexes et variées.

Plan du cours

Pour traiter les nombreuses erreurs qui subsistent à l'exécution, la bibliothèque standard de C dispose d'un fichier d'en-tête spécialisé dans la gestion des erreurs : <errno.h>. Cette partie de la bibliothèque standard, ainsi que certaines fonctions situées dans des fichiers d'en-têtes différents (<string.h>, <stdio.h>) fera l'objet de la première partie du cours, plus théorique, qui résume les pages de documentation et les principes de base d'un programme C gérant basiquement les erreurs

La suite du cours sera constitué d'un débat, plus ou moins subjectif, sur la remontée des erreurs. Nous analyserons les deux grandes méthodes de remontée des erreurs (un seul point de sortie par fonction ou plusieurs), et nous redécouvrirons le subtil goto, qui trouve presque toute son utilité dans la gestion des erreurs. Enfin, nous examinerons les sauts non-locaux, souvent délaissés mais parfois utiles.

Pour terminer, la troisième partie sera plus pratique. Nous créerons une petite bibliothèque de gestion des erreurs. Dans un premier temps, nous créerons les fondements de la bibliothèque : initialisation, affichage d'un message d'erreur selon un niveau d'erreur, fermeture. Dans un second temps, nous créerons un système de stacktrace de la pile d'appel, qui, à l'aide de mots-clés transformés, permettront au programmeur d'afficher la pile d'appel des fonctions de son programme. Ce sera sans doute la partie la plus technique, puisqu'elles feront appel à une partie plus algorithmique de la programmation en C. Enfin, dans un troisième temps, nous créerons notre propre système d'erreurs courantes, une sorte d'errno en plus complet, en leur associant un descriptif et en reproduisant un mécanisme d'exception.

C'est donc le programme qui nous attend. Je ne vous en dis pas plus, la suite est dans le reste du cours. Bon courage !

[Théorie] Gestion simple et standard des erreurs

Dans cette première partie, nous analyserons les fonctions et autres outils que nous propose la bibliothèque standard de C pour réaliser une gestion des erreurs, certes sommaire, mais nécessaire. Après un bref retour sur le flux d'erreurs standard stderr et son utilité, nous étudierons l'unité de base de la gestion des erreurs : errno, puis nous manipulerons et interpréterons son contenu à l'aide d'autres fonctions externes.

Le flux d'erreurs standard stderr

Le flux d'erreurs standard stderr se modélise par un fichier dans lequel on peut écrire nos erreurs. Grâce à cette écriture séparée, l'utilisateur pourra rediriger le flux standard afin d'isoler les erreurs et ainsi mieux les traiter, en nombre moins importants, devant la masse d'informations contenues en sortie. Bien que ce passage peut sembler inutile à certains, il me semblait important de revenir sur la notion de flux, qui, au-delà de l'aspect de culture générale informatique qui y est associé, vous permettra de comprendre les trois flux standards qui régissent l'utilisation d'un processus d'un programme écrit en langage C.

Définition d'un flux

Les flux peuvent être représentés comme des canaux dans lesquels circulent des informations. Dans l'informatique moderne, ces flux sont représentés par l'abstraction de base du disque dur : le fichier. Ainsi, tout ce qui sera écrit dans un des flux sera écrit dans le fichier associé. A l'origine modélisés sur des systèmes d'exploitation de type UNIX, cette technique s'est largement généralisée auprès des systèmes d'exploitation modernes. D'une manière plus générale, on peut associer la notion de flux à d'autres flux plus renommés : flux RSS, flux de paquets, etc.

Présentation des trois flux standards

En programmation, l'utilité principale des flux réside dans les entrées/sorties. L'abstraction qu'est le processus nous permet de distinguer trois flux standards : le flux d'entrée standard (stdin), le flux de sortie standard (stdout) et le flux de sortie d'erreurs standard (stderr). Le fichier d'en-tête <stdio.h> déclare ces identificateurs comme étant de type pointeurs sur FILE.

Les trois flux standards (Wikipedia)

Ces trois descripteurs de fichiers sont initialisés au lancement du processus, et sont libérés à sa destruction. Il n'est donc pas nécessaire de les ouvrir et de les fermer comme on pourrait le faire avec des fichiers classiques.

Le flux d'entrée standard est tout ce qui est envoyé en entrée au programme. La plupart du temps, cela prend la forme d'informations écrites au clavier. Toutefois, comme nous le verrons dans la partie suivante, l'entrée standard peut prendre une forme différente dans le cadre d'une redirection.

Le flux de sortie standard et le flux de sortie d'erreurs standard sont par défaut associés à la console. Là encore, il est possible de les isoler l'un de l'autre, afin de différencier messages d'informations, envoyés à la sortie standard, et messages d'erreurs ou avertissements, envoyés à la sortie d'erreurs standards.

Il est possible de vider le tampon associé à un flux, c'est-à-dire de forcer l'écriture de toutes les données contenues dans celui-ci, à l'aide la fonction fflush. Attention, appeler fflush avec comme argument un flux d'entrée comme stdin est un comportement indéterminé.

Utilité de la redirection d'un flux standard

L'utilité concrète de ces trois flux est attribuée à l'utilisateur. En effet, celui-ci peut rediriger un ou plusieurs des trois flux vers un fichier. L'objet de ce cours n'étant pas de manipuler les différentes commandes consoles, nous ne nous étalerons pas sur les différents outils de redirection, vous laissant le droit d'en savoir plus grâce à une rapide recherche.

De manière succincte, nous pouvons signaler trois sigles permettant de rediriger les trois flux standards. Le chiffre « 1 » et le chevron fermant : « 1> » redirige la sortie standard vers un fichier passé en paramètre à la suite du symbole. Le chiffre « 2 » et le chevron fermant : « 2> » redirige la sortie d'erreurs standards vers le fichier. Le chevron ouvrant « < » redirige quant à lui l'entrée standard vers un fichier.

Par exemple, prenons le code source du célèbre programme « Hello world » qui affiche le message éponyme sur la sortie standard.

#include <stdio.h> int main(void) { printf("hello world\n"); return 0; } $ gcc test.c -o hello.out $ ./hello.out 1> hello.txt $ cat hello.txt hello world !

Ces essais ont été réalisés sous GNU/Linux. Sous Windows, le fonctionnement est à peu près similaire.

L'utilité concrète est multiple. Premièrement, la redirection du flux d'entrée standard peut permettre de soumettre à votre programme une batterie de tests. Ainsi, vous n'avez pas à retaper insatiablement les mêmes entrées. Deuxièmement, la redirection du flux de sortie standard peut permettre d'enlever tous les messages inutiles que certaines applications affichent. Troisièmement, la redirection de flux de sortie d'erreurs standard peut permettre à l'utilisateur d'isoler les erreurs, et ainsi de les traiter pertinemment.

Vous pouvez vous renseigner sur la fonction freopen pour rediriger un des flux standards vers un fichier directement dans votre programme.

La variable globale errno

errno est le point de départ de la gestion des erreurs standard offerte par la bibliothèque standard de C. C'est une variable globale déclarée dans <errno.h>, n'oubliez donc pas d'inclure ce fichier d'en-tête ultérieurement.

Présentation de errno

errno prend généralement la forme d'une variable globale de type int.

Avec les nouvelles technologies de multi-threading, errno est très souvent implémentée sous forme de macro. N'essayez donc pas de récupérer son adresse.

Contenu standard possible de errno

Elle contient une valeur correspondant au code de la dernière erreur s'étant produite. Hélas, on voit là les limites d'errno, puisque la bibliothèque standard ne définit que trois codes d'erreur : EDOM (passage en paramètre en dehors du domaine attendu), ERANGE (résultat trop grand ou trop petit) et EILSEQ (erreur de transcodage).

Extensions du contenu de errno

Heureusement, les systèmes d'exploitation communs et actuels proposent souvent certaines extensions au contenu de errno. La norme POSIX définit par exemple une trentaine de constantes numériques supplémentaires (elles sont ici).

Interprétation du contenu de errno

Il est donc difficile d'associer directement le contenu de errno à des codes d'erreur, car la portabilité risque de nous faire défaut. C'est pourquoi la bibliothèque standard de C met à notre disposition deux fonctions qui interprètent le contenu de errno, et nous évitent ainsi de passer par un code non standard, ou bien considérablement allongé.

strerror

La fonction strerror associe au code d'erreur passé en paramètre une description de celui-ci en sortie. La fonction est déclarée comme suit dans le fichier d'en-tête de la bibliothèque standard <string.h>.

char *strerror(int errnum);

Son utilisation est assez simple. Par exemple, prenons la fonction strtol, qui convertit une chaîne de caractère en nombre. Le standard indique que si le résultat dépasse les limites du type, LONG_MIN ou LONG_MAX est retourné.

#include <errno.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { char s[32]; snprintf(s, sizeof s, "%lu", LONG_MAX + 1UL); if (strtol(s, NULL, 10) == LONG_MAX) fprintf(stderr, "%s\n", strerror(errno)); return 0; } Numerical result out of range

Mais que se passe-t-il si nous essayons de convertir la valeur LONG_MAX ?

#include <errno.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { char s[32]; snprintf(s, sizeof s, "%ld", LONG_MAX); if (strtol(s, NULL, 10) == LONG_MAX) fprintf(stderr, "%s\n", strerror(errno)); return 0; } Success

Dans ce cas-là, il nous faut interpréter le contenu d'errno pour savoir si il faut afficher une erreur ou pas.

#include <errno.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { char s[32]; snprintf(s, sizeof s, "%llu", LONG_MAX + 1ULL); errno = 0; if (strtol(s, NULL, 10) == LONG_MAX && errno) fprintf(stderr, "%s\n", strerror(errno)); return 0; } perror

L'autre fonction que je tenais à vous présenter est encore plus simple d'usage (et elle est aussi plus utilisée). Il s'agit de la fonction perror, qui associe à la valeur courante de errno sa description, l'affichant sur la sortie d'erreurs standard. Il est également possible de placer un préfixe devant cette description, que l'on pourra passer en paramètre.

void perror(const char *s);

On reprend le même exemple que tout à l'heure :

#include <errno.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> int main(void) { char s[32]; snprintf(s, sizeof s, "%lu", LONG_MAX + 1UL); errno = 0; if (strtol(s, NULL, 10) == LONG_MAX && errno) perror("strtol"); return 0; } strtol: Numerical argument out of domain

Pour clore cette sous-partie, sachez que vous pouvez également profiter des variables globales sys_errlist (tableau de chaînes de caractère indiquant les différentes descriptions pouvant être indexées par errno) et sys_nerr (nombre de codes d'erreur supportés par le système). Attention, ces deux variables ne sont que conformes BSD.

[Débat] La remontée des erreurs

Après cette première sous-partie particulièrement théorique, vous aurez le droit à une deuxième sous-partie un peu plus subjective, basée sur des débats qui régissent l'utilisation du langage C sur les forums. Chacun est libre d'avoir son propre avis, les thèses qui seront présentées ici seront simplement basées sur l'expérience globale des personnes ayant participé à ce tutoriel. Nous resterons cependant le plus objectif possible.

SESE vs SEME

Dans la programmation depuis toujours, on constate souvent que deux thèses s'opposent quant au nombre de points de sortie (return) qu'une fonction peut avoir. Certains prônent la lisibilité, au profit de la règle SESE, et d'autres préfèrent avoir un code moins complexe, avec la règle SEME.

SEME

Commençons par celle qui peut vous sembler la plus naturelle : les multiples points de sortie. Pour les exemples, nous prendrons une fonction qui alloue deux éléments dans une structure retournée en tant que pointeur. C'est sans doute le plus marquant pour différencier les deux techniques. Avec la théorie SEME, voici ce que cela pourrait donner :

#define TABSIZE 32 #define NAMESIZE 24 typedef struct { int *tab; char *name; } board; board *allocBoard(void) { board *this; if (!(this = malloc(sizeof *this))) return NULL; if (!(this->tab = malloc(TABSIZE * sizeof *this->tab))) { free(this); return NULL; } if (!(this->name = malloc(NAMESIZE))) { free(this->tab); free(this); return NULL; } return this; }

Un code assez simple qui devrait quand même faire frémir quelques...

Appeler le centre

Avez-vous besoin d'un coach de formation?

Il vous aidera à comparer différents cours et à trouver la solution la plus abordable.

La gestion des erreurs en C

Prix sur demande