Pourquoi ne pas vérifier si errno contient le code d’erreur ERANGE?

J’ai essayé de convertir correctement un tableau de caractères en long avec strtol , de vérifier s’il y avait un débordement ou un dépassement strtol , puis de faire une conversion int longue. En chemin, j’ai remarqué beaucoup de code qui ressemble à ceci

 if ((result == LONG_MAX || result == LONG_MIN) && errno == ERANGE) { // Handle the error } 

Pourquoi ne pouvez-vous pas simplement dire

 if(errno == ERANGE) { // Handle the error } 

À ma connaissance, en cas de débordement ou de dépassement, errno contient le code d’erreur ERANGE dans les deux cas. Alors, le premier est-il vraiment nécessaire? Est-ce que vérifier ERANGE seul peut être problématique?

Voici à quoi ressemble mon code

  char *endPtr; errno = 0; long result = strtol(str, &endPtr, 10); if(errno == ERANGE) { // Handle Error } else if(result > INT_MAX || result < INT_MIN) { // Handle Error } else if(endPtr == str || *endPtr != '\0') { // Handle Error } num = (int)result; return num; 

S’il y a une raison pour le premier s’il vous plaît faites le moi savoir.

    Le premier extrait de code est tout simplement faux, et j’expliquerai pourquoi plus tard, mais nous avons d’abord besoin d’un peu de contexte.

    errno est une variable locale au thread . Il est défini sur une valeur autre que zéro en cas d’échec d’un appel système ou de certaines fonctions de la bibliothèque. Il rest inchangé lorsqu’un appel système réussit. Donc, il contient toujours le numéro d’erreur du dernier appel ayant échoué.

    Cela signifie que vous avez deux choix. Définissez errno sur 0 avant chaque appel ou utilisez l’idiome standard pour errno . Voici le pseudo-code pour l’idiome standard

     if ( foo() == some_value_that_indicates_that_an_error_occurred ) then the value in errno applies to foo else foo succeeded and the errno must be ignored because it could be anything 

    La plupart des programmeurs utiliseront l’idiome standard, car définir errno sur 0 avant chaque appel système est ennuyant, répétitif, répétitif, ennuyeux et répétitif. Sans oublier le fait que vous pourriez oublier de définir errno sur 0 au seul endroit qui compte vraiment.


    Retour au premier extrait de code. C’est faux car il n’y a pas de valeur de retour de strtol qui indique sans ambiguïté que strtol échoué. Si strtol retourne LONG_MAX , il se peut qu’une erreur se soit produite ou que la chaîne contienne le numéro LONG_MAX . Il n’y a aucun moyen de savoir si l’appel strtol réussi ou échoué. Ce qui signifie que l’idiome standard (qui correspond à ce que le premier fragment de code tente d’implémenter) ne peut pas être utilisé avec strtol .

    Pour utiliser correctement strtol , vous devez définir errno sur 0 avant l’appel, comme ceci

     errno = 0; result = strtol( buffer, &endptr, 10 ); if ( errno == ERANGE ) { // handle the error // ERANGE is the only error mentioned in the C specification } else if ( endptr == buffer ) { // handle the error // the conversion failed, ie the input ssortingng was empty, // or only contained whitespace, or the first non-whitespace // character was not valid } 

    Notez que certaines implémentations définissent d’autres valeurs non nulles pour errno . Voir la page de manuel applicable pour plus de détails.

    Si vous appelez

     result = strtol("-2147483648", NULL, 0); 

    ou

     result = strtol("2147483647", NULL, 0); 

    sur un ordinateur 32 bits, vous obtiendrez LONG_MIN ou LONG_MAX dans le result , même s’il n’ya pas eu d’erreur.

    Comme l’explique l’utilisateur 3386109, un moyen de détecter les erreurs de strtol consiste à définir errno sur 0 en premier. L’autre méthode consiste à vous laisser un pointeur de fin et à regarder cela. Il y a trois ou quatre cas:

     char *endptr; long int result = strtol(str, &endptr, 10); if(*str == '\0') { /* str was empty */ } else if(endptr == str) { /* str was completely invalid */ } else if(*endptr != '\0') { /* numeric result followed by trailing nonnumeric character(s) */ } else { /* str was a completely valid number (perhaps with leading whitespace) */ } 

    Selon vos besoins, les deux ou trois premiers cas peuvent être regroupés. Vous devrez alors peut-être vous demander (a) si le “nombre complètement valide” était représentable (ce que vous pouvez tester avec errno ), et b) si un “caractère non numérique suivi (s)” était un blanc inoffensif (qui, hélas, strtol ne vérifie pas pour vous, donc si vous y tenez, vous devrez vous contrôler vous-même).