Errno et la gestion d'erreur en C
- Mia Combeau
- C
- 1 novembre 2022
Table des matières
Détecter les erreurs dans un programme ainsi que leur cause, c’est la base de tout développement informatique. Mais quand une fonction ou un appel système d’une bibliothèque externe échoue, comment peut-on comprendre ce qu’il s’est passé ? Ces fonctions renvoient généralement une valeur de -1 ou NULL
, mais elles stockent aussi la raison de leur échec dans errno.
Voyons donc ce qu’est errno et comment le récupérer, l’interpréter et l’imprimer.
Qu’est-ce qu’errno ?
Errno (“error number” ou “code erreur” en français) est une variable de type entier stocké dans la bibliothèque <errno.h>
. Quand un appel système échoue, il y place systématiquement son code d’erreur pour expliquer les conditions de son échec. Beaucoup d’autres fonctions des bibliothèques standard font de même. Le code erreur dans cette variable peut alors être interprété pour comprendre ce qu’il s’est mal passé.
Au début d’un programme, errno commence toujours par avoir une valeur de 0, ce qui indique le succès. Par contre, une fois que cette variable est modifiée pour contenir un code d’erreur, elle ne retournera jamais à 0. En effet, aucun appel système ou fonction standard ne stocke jamais 0 dans errno. C’est pourquoi sa valeur n’est significative qu’immédiatement après l’échec d’un appel système ou d’une fonction standard. C’est à dire après avoir reçu généralement -1 ou NULL
en retour de fonction.
Pourtant, certaines fonctions peuvent renvoyer -1 ou NULL
en cas de succès ou d’erreur. C’est le cas des fonctions readdir et getpriority. Il est dans ces cas important de remettre errno à 0 avant l’appel de fonction pour pouvoir vérifier si une erreur s’est produite pendant l’appel. Pour lui assigner une valeur, rien de plus simple :
errno = 0;
Les nombres stockés dans errno pour représenter des erreurs diverses sont associées à des macros qui leur fournit un nom plus descriptif, ainsi qu’à une courte phrase qui décrit l’erreur en question. Par exemple, le code d’erreur 13
est associé à la macro EACCES
et à la phrase descriptive “Permission denied”.
Errno a plus d’une centaine de codes d’erreur décrits. Alors quels sont tous ces codes d’erreur ? Nous pouvons tous les voir avec la commande errno dans les systèmes de type Unix.
La commande Unix errno
La commande errno est un outil très utile qui permet de lister ou de rechercher ses codes d’erreur. Elle est disponible dans les systèmes de type Unix avec le paquet moreutils qu’on peut installer de cette façon s’il ne l’est pas déjà :
$ sudo apt install moreutils
Pour afficher la liste de tous les codes erreur avec leur nom de macro et descriptions correspondantes, il nous suffit maintenant de lancer la commande :
$ errno -l
EPERM 1 Operation not permitted
ENOENT 2 No such file or directory
ESRCH 3 No such process
EINTR 4 Interrupted system call
EIO 5 Input/output error
ENXIO 6 No such device or address
E2BIG 7 Argument list too long
ENOEXEC 8 Exec format error
EBADF 9 Bad file descriptor
ECHILD 10 No child processes
EAGAIN 11 Resource temporarily unavailable
ENOMEM 12 Cannot allocate memory
EACCES 13 Permission denied
EFAULT 14 Bad address
(...)
On peut aussi chercher une erreur par son code numérique ou par sa macro :
$ errno 21
EISDIR 21 Is a directory
$ errno ENOENT
ENOENT 2 No such file or directory
Ou encore, rechercher toutes les erreurs qui contiennent un certain mot dans leur description avec l’option -s
:
$ errno -s directory
ENOENT 2 No such file or directory
ENOTDIR 20 Not a directory
EISDIR 21 Is a directory
ENOTEMPTY 39 Directory not empty
Mais ne serait-il pas utile de pouvoir afficher la description de l’erreur rencontrée depuis nos programmes en C ? Voyons comment faire.
Afficher l’erreur d’errno
Il y a deux fonctions qui nous permettent de traduire le code d’erreur numérique contenu dans errno en sa phrase descriptive correspondante pour l’afficher dans notre terminal : perror
et strerror
.
Afficher l’erreur avec perror
La fonction perror
de la bibliothèque <stdio.h>
a pour prototype :
void perror(const char *s);
Appelée suite à une erreur, la fonction perror
imprime sur la sortie d’erreur ( descripteur de fichier 2) la chaîne de caractères s qu’on lui indique suivie de deux-points puis de la chaîne de caractères correspondant à la valeur actuelle dans errno.
Par exemple, tentons d’ouvrir un fichier qui n’existe pas et d’utiliser perror
après avoir détecté le retour d’échec de la fonction open
:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int fd;
fd = open("does_not_exist", O_RDONLY);
if (fd == -1)
{
perror("open");
return (1);
}
close(fd);
return (0);
}
Afficher l’erreur avec strerror
Avec la fonction strerror
de la bibliothèque <string.h>
, on a plus de flexibilité sur la présentation du message d’erreur puisqu’elle ne fait que convertir le nombre d’errno en chaîne de caractères. Voici son prototype :
char *strerror(int errnum);
On peut simplement lui indiquer la variable errno dans son paramètre errnum pour transformer l’entier en la chaîne de caractères correspondante. La fonction strerror
n’affiche rien elle-même, elle renvoie simplement la chaîne de caractères appropriée. Avec cette fonction, nous devons donc imprimer l’erreur nous mêmes, de préférence sur la sortie d’erreur (fd 2).
Tentons de nouveau d’ouvrir un fichier qui n’existe pas et imprimons l’erreur avec la fonction write
après avoir convertit errno en chaîne de caractères avec strerror
:
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int fd;
fd = open("does_not_exist", O_RDONLY);
if (fd == -1)
{
write(2, strerror(errno), strlen(strerror(errno)));
write(2, "\n", 1);
return (1);
}
close(fd);
return (0);
}
Une autre astuce à partager, une petite question à poser, ou une découverte intéressante à propos d’errno ou de la gestion d’erreur en C ? Je serai ravie de lire et de répondre à tout ça dans les commentaires. Bon code !
Sources et lectures supplémentaires
- Manuel du programmeur Linux :
- Rentes, M., moreutils: the utilities package every UNIX/Linux/Mac OS developer should know [rentes.github.io]