58 votes

Est-ce que std :: stoi est réellement sans danger?

J'ai eu une belle conversation avec quelqu'un à propos de la chute de std::stoi. Pour dire les choses crûment, il utilise std::strtol en interne, et jette si qui signale une erreur. Selon eux, même si, std::strtol ne devraient pas signaler une erreur pour une entrée de "abcxyz", provoquant stoi ne pas jeter std::invalid_argument.

Tout d'abord, voici deux programmes testés sur GCC sur les comportements de ces cas:
strtol
stoi

Tous les deux montrent le succès sur "123" et échec sur "abc".


J'ai regardé dans le standard de tirer plus d'infos:

§ 21.5

Throws: invalid_argument if strtol, strtoul, strtoll, or strtoull reports that  
no conversion could be performed. Throws out_of_range if the converted value is  
outside the range of representable values for the return type.

Qui résume le comportement de s'appuyer sur strtol. Maintenant que diriez - strtol? J'ai trouvé ça dans le C11 projet:

§7.22.1.4

If the subject sequence is empty or does not have the expected form, no  
conversion is performed; the value of nptr is stored in the object  
pointed to by endptr, provided that endptr is not a null pointer.

Compte tenu de la situation du passage dans "abc", la norme stipule que nptr, qui pointe vers le début de la chaîne, serait stocké dans endptr, le pointeur passé en. Cela semble cohérent avec le test. Aussi, 0 doit être retourné, comme l'a déclaré ceci:

§7.22.1.4

If no conversion could be performed, zero is returned.

La référence précédente, a déclaré qu'aucune conversion est effectuée, de sorte qu'il doit retourner 0. Ces conditions d'aujourd'hui se conformer avec le C++11 standard pour stoi jetant std::invalid_argument.


Le résultat de ce qui est important pour moi parce que je ne veux pas aller autour de recommander stoi comme une meilleure alternative à d'autres méthodes de string en int la conversion, ou de l'utiliser moi-même comme si elle a travaillé la façon dont vous vous attendez, si il ne l'attrape pas le texte comme une défaillance de la conversion.

Donc, après tout, ne suis-je trompé quelque part? Il me semble que j'ai une bonne preuve de cette exception levée. Est ma preuve valide, ou est - std::stoi n'est pas garanti que cette exception générée lorsqu'ils sont administrés "abc"?

73voto

Generic Human Points 2856

N' std::stoi jeter une erreur sur l'entrée "abcxyz"?

Oui.

Je pense que votre confusion peut venir du fait qu' strtol jamais des rapports d' erreur à l'exception de débordement. Il peut signaler qu'aucune conversion n'a été réalisée, mais ce n'est jamais mentionné comme une condition d'erreur dans le C standard.

strtol est défini de la même manière par tous les trois normes C, et je vais vous épargner les détails ennuyeux, mais essentiellement, il définit un "séquence" qui est une sous-chaîne de la chaîne d'entrée correspondant au nombre réel. Les quatre conditions suivantes sont équivalentes:

  • le sujet de la séquence a la forme attendue (en anglais: c'est un nombre)
  • le sujet de la séquence non vide
  • une conversion a eu lieu
  • *endptr != nptr (ce qui n'a de sens que lorsqu' endptr est non-nulle)

Quand il y a un dépassement, la conversion est tout de même dit avoir eu lieu.

Maintenant, il est tout à fait clair que, parce qu' "abcxyz" ne contient pas un nombre, le sujet de la séquence de la chaîne "abcxyz" doit être vide, de sorte qu'aucune conversion peut être effectuée. La suite C90/C99/C11 programme qui va confirmer expérimentalement:

#include <stdio.h>
#include <stdlib.h>

int main() {
    char *nptr = "abcxyz", *endptr[1];
    strtol(nptr, endptr, 0);
    if (*endptr == nptr)
        printf("No conversion could be performed.\n");
    return 0;
}

Ceci implique que toute conforme à la mise en œuvre de l' std::stoi doit jeter invalid_argument lorsque compte tenu de l'entrée "abcxyz" sans une option de base de l'argument.


Est-ce à dire qu' std::stoi a satisfaisant de vérification d'erreur?

Pas de. La personne que vous parler est correcte quand elle dit que l' std::stoi est plus indulgent que d'effectuer la vérification complète errno == 0 && end != start && *end=='\0' après std::strtolcar std::stoi silencieusement supprime tous les caractères à partir du premier caractère non numérique dans la chaîne.

En fait sur le dessus de ma tête, la seule langue dont natif de conversion se comporte un peu comme std::stoi Javascript est, et même alors, vous avez à la force de la base de 10 parseInt(n, 10) afin d'éviter le cas particulier des nombres hexadécimaux:

input      |  std::atoi       std::stoi      Javascript      full check 
===========+=============================================================
hello      |  0               error          error(NaN)      error      
0xygen     |  0               0              error(NaN)      error      
0x42       |  0               0              66              error      
42x0       |  42              42             42              error      
42         |  42              42             42              42         
-----------+-------------------------------------------------------------
languages  |  Perl, Ruby,     Javascript     Javascript      C#, Java,  
           |  PHP, C...       (base 10)                      Python...  

Remarque: il existe également des différences entre les langues dans la gestion d'espaces et redondante + signes.


Ok, donc je veux plein de vérification d'erreur, que dois-je utiliser?

Je ne suis pas au courant d'une fonction intégrée qui fait cela, mais boost::lexical_cast<int> à faire ce que vous voulez. Il est particulièrement stricte puisqu'elle même rejette entourant espace, contrairement à Python int() fonction. Notez que les caractères non valides et les débordements de suite dans la même exception, boost::bad_lexical_cast.

#include <boost/lexical_cast.hpp>

int main() {
    std::string s = "42";
    try {
        int n = boost::lexical_cast<int>(s);
        std::cout << "n = " << n << std::endl;
    } catch (boost::bad_lexical_cast) {
        std::cout << "conversion failed" << std::endl;
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X