« Programmation C/Erreurs » : différence entre les versions

Un livre de Wikilivres.
Contenu supprimé Contenu ajouté
Tpierron (discussion | contributions)
Tpierron (discussion | contributions)
Ligne 22 : Ligne 22 :


== Exemple ==
== Exemple ==
Pour illustrer cela, voici un exemple, qui tente de convertir une chaine de caractères en un nombre. Le nombre donné, 2<sup>64</sup>, peut ne pas être dans le domaine de valeur du type '''unsigned long''' (qui peut se limiter à 2<sup>32</sup> - 1), suivant l'implémentation qui est utilisée pour l'exécuter. Dans ce cas, on utilise '''errno''' pour afficher un message d'erreur approprié à l'utilisateur.
Pour illustrer cela, voici un exemple, qui tente de convertir une chaine de caractères en un nombre. Le nombre donné, 2<sup>64</sup>, peut ne pas être dans le domaine de valeur du type '''unsigned long''' (qui peut se limiter à 2<sup>32</sup> - 1), suivant l'architecture. Dans ce cas, on utilise '''errno''' pour afficher un message d'erreur approprié à l'utilisateur.


<pre>
<pre>
Ligne 28 : Ligne 28 :
#include <stdlib.h> /* strtoul() */
#include <stdlib.h> /* strtoul() */
#include <limits.h> /* ULONG_MAX */
#include <limits.h> /* ULONG_MAX */
#include <errno.h> /* errno et ERANGE */
#include <string.h> /* strerror() */
#include <errno.h> /* errno */


void teste(const char *nombre)
void teste(const char *nombre)
Ligne 37 : Ligne 38 :
{
{
/* il y a eu une erreur ! */
/* il y a eu une erreur ! */
printf("Impossible de convertir le nombre '%s': %s.\n", nombre, strerror(errno));
if (errno == ERANGE)
{
/* on sait quelle est l'erreur */
puts("L'erreur rencontree est une erreur de domaine.");
}
else
{
/* pas d'information précise à fournir à l'utilisateur */
puts("Une erreur a ete rencontree.");
}
}
}
else
else
{
{
printf("La conversion a ete effectuee, et la valeur est: %ld\n", res);
printf("La conversion a été effectuée, et la valeur est: %ld\n", res);
}
}
}
}
Ligne 66 : Ligne 58 :
ULONG_MAX vaut 4294967295l.
ULONG_MAX vaut 4294967295l.
La conversion a ete effectuee, et la valeur est: 256
La conversion a ete effectuee, et la valeur est: 256
Impossible de convertir le nombre '18446744073709551616': la valeur est en dehors de l'intervalle du type spécifié.
L'erreur rencontree est une erreur de domaine.


Il est nécessaire de placer '''errno''' à 0 ''avant'' d'utiliser '''strtoul''', car les fonctions de la bibliothèque standard ne sont pas obligées de la mettre à zéro en cas de succès. À cause de celà, si on ne la réinitialisait pas «&nbsp;manuellement&nbsp;», on pourrait voir le résultat d'une erreur précédente, causée par une autre fonction. C'est une des lourdeurs du modèle de gestion d'erreur du C...
Il est nécessaire de placer '''errno''' à 0 ''avant'' d'utiliser '''strtoul''', car les fonctions de la bibliothèque standard ne sont pas obligées de la mettre à zéro en cas de succès. Si on ne la réinitialisait pas «&nbsp;manuellement&nbsp;», on pourrait voir le résultat d'une erreur causée par un appel de fonction antérieur. C'est une des lourdeurs du modèle de gestion d'erreur du C, même si une telle paranoïa n'est pas toujours nécessaire.

'''Note&nbsp;:''' Le contrôle d'erreur des fonctions '''strto*''' est en réalité plus complexe, puisque le deuxième paramètre permet un contrôle plus fin. Il n'est pas utilisé ici, pour se concentrer sur '''errno''', mais devrait l'être dans un vrai programme.


== Erreurs possibles ==
== Erreurs possibles ==

Version du 21 juin 2006 à 03:35


Le langage C fournit un en-tête spécialisé pour la gestion des erreurs : errno.h. Cet en-tête déclare notamment une variable globale errno, et un certain nombre de codes d'erreur, qui permettent aux fonctions de la bibliothèque standard de reporter précisément la cause d'une erreur.

Utilisation

Pour inclure l'en-tête de son fichier source, il faut ajouter la ligne :

#include <errno.h>

On peut alors utiliser la variable errno, de type int, pour traiter les erreurs[1].

Lorsqu'on veut utiliser errno pour déterminer la cause d'un échec, il faut d'abord s'assurer que la fonction a bel et bien échouée. Le C laissant une certaine liberté dans la manière de signaler un échec, il n'y a pratiquement aucun mécanisme universel qui permet de détecter une telle situation, chaque fonction étant presque un cas particulier. Cepandant, une pratique relativement répandue est de retourner un code spécifique, en général en dehors de l'intervalle de ce qu'on s'attend. Par exemple lorsque la fonction retourne un pointeur, pour allouer un objet, une erreur est souvent signalée en retournant le pointeur générique NULL et la variable errno décrit plus en détail la nature de l'erreur.

Une fois qu'on est sûr que la variable errno contient une valeur pertinente, il est indispensable de présenter un message tout aussi significatif à l'utilisateur. Rien n'est plus frustrant qu'un message aussi abscon que : "fopen("/tmp/tmpab560f9d4", "w") : erreur 28".

Énumérer toutes les valeurs possibles d'errno peut être relativement pénible, surtout si on doit le faire dans chaque application. Heureusement qu'une fonction toute prête existe :

#include <string.h>
char * strerror(int code);

Cette fonction permet de connaître la signification textuelle d'une valeur de errno. À noter que le code de retour est une chaine statique, dont il sage de présupposer la durée de vie la plus courte possible. N'essayer pas non plus de modifier le contenu de la chaîne, afficher la directement ou copier la dans une zone temporaire.

Un autre intérêt de passer par cette fonction, c'est qu'elle est en général localisée, ce qui est toujours ça de moins à traduire, surtout lorsqu'on voit la pléthore d'erreurs possibles que peuvent générer ne serait-ce que les fonctions d'entrées/sorties.

Exemple

Pour illustrer cela, voici un exemple, qui tente de convertir une chaine de caractères en un nombre. Le nombre donné, 264, peut ne pas être dans le domaine de valeur du type unsigned long (qui peut se limiter à 232 - 1), suivant l'architecture. Dans ce cas, on utilise errno pour afficher un message d'erreur approprié à l'utilisateur.

#include <stdio.h>  /* puts(), printf(), NULL */
#include <stdlib.h> /* strtoul() */
#include <limits.h> /* ULONG_MAX */
#include <string.h> /* strerror() */
#include <errno.h>  /* errno */

void teste(const char *nombre)
{
    errno = 0;
    unsigned long res = strtoul(nombre, NULL, 10);
    if (res == ULONG_MAX)
    {
        /* il y a eu une erreur ! */
        printf("Impossible de convertir le nombre '%s': %s.\n", nombre, strerror(errno));
    }
    else
    {
        printf("La conversion a été effectuée, et la valeur est: %ld\n", res);
    }
}

int main(void)
{
    /* 2 puissance 8 : sera toujours accepte */
    teste("256");

    /* 2 puissance 64 : peut echouer suivant la machine */
    teste("18446744073709551616");
    return 0;
}

Si unsigned long est codé sur 32 bits (c'est le cas sur la plupart des architectures de types PC en 32 bits), on peut obtenir:

ULONG_MAX vaut 4294967295l.
La conversion a ete effectuee, et la valeur est: 256
Impossible de convertir le nombre '18446744073709551616': la valeur est en dehors de l'intervalle du type spécifié.

Il est nécessaire de placer errno à 0 avant d'utiliser strtoul, car les fonctions de la bibliothèque standard ne sont pas obligées de la mettre à zéro en cas de succès. Si on ne la réinitialisait pas « manuellement », on pourrait voir le résultat d'une erreur causée par un appel de fonction antérieur. C'est une des lourdeurs du modèle de gestion d'erreur du C, même si une telle paranoïa n'est pas toujours nécessaire.

Erreurs possibles

Les codes d'erreurs suivants sont définis :

  • EDOM
  • EILSEQ
  • ERANGE

Une implémentation peut fournir, en plus des 3 précédents, autant de valeurs pour errno qu'elle le souhaite. Ci-dessous se trouve une liste d'erreurs présentes sur différents systèmes:

  • E2BIG
  • EACCES
  • EADDRINUSE
  • EADDRNOTAVAIL
  • EAFNOSUPPORT
  • EAGAIN
  • EALREADY
  • EBADF
  • EBADMSG
  • EBUSY
  • ECANCELED
  • ECHILD
  • ECONNABORTED
  • ECONNREFUSED
  • ECONNRESET
  • EDEADLK
  • EDESTADDRREQ
  • EDQUOT
  • EEXIST
  • EFAULT
  • EFBIG
  • EHOSTUNREACH
  • EIDRM
  • EINPROGRESS
  • EINTR
  • EINVAL
  • EIO
  • EISCONN
  • EISDIR
  • ELOOP
  • EMFILE
  • EMLINK
  • EMSGSIZE
  • EMULTIHOP
  • ENAMETOOLONG
  • ENETDOWN
  • ENETRESET
  • ENETUNREACH
  • ENFILE
  • ENOBUFS
  • ENODATA
  • ENODEV
  • ENOENT
  • ENOEXEC
  • ENOLCK
  • ENOLINK
  • ENOMEM
  • ENOMSG
  • ENOTPROTOOPT
  • ENOSPC
  • ENOSR
  • ENOSTR
  • ENOSYS
  • ENOTCON
  • ENOTDIR
  • ENOTEMPTY
  • ENOTSOCK
  • ENOTSUP
  • ENOTTY
  • ENXIO
  • EOPNOTSUPP
  • EOVERFLOW
  • EPERM
  • EPIPE
  • EPROTO
  • EPROTONOSUPPORT
  • EPROTOTYPE
  • EROFS
  • ESPIPE
  • ESRCH
  • ESTALE
  • ETIME
  • ETIMEDOUT
  • ETXTBSY
  • EWOULDBLOCK
  • EXDEV

Notes

  1. errno peut être définie par une macro ou un identificateur. N'essayez pas de récupérer son adresse.