Menu

Chaîne vers int en C : atoi, strtol et la conversion de nombres en texte

Convertir entre texte et nombres en C : pourquoi atoi ne peut pas signaler d'erreur, comment utiliser strtol et strtod correctement avec endptr et errno, transformer des nombres en chaînes avec snprintf, et les astuces sur un chiffre avec '0'.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

Le texte arrive des utilisateurs, des fichiers et de la ligne de commande sous forme de caractères. "42" fait trois octets - '4', '2', '\0' - et rien dedans n'est le nombre 42 tant que vous ne l'avez pas converti. Le C offre deux générations de fonctions de conversion : les anciennes, courtes, qui ne peuvent pas signaler d'erreur, et les plus longues, qui le peuvent. Cette page montre les deux et explique pourquoi les vrais programmes utilisent la seconde.

L'ancienne famille : atoi, atol, atof

Elles se lisent comme leurs noms : ASCII vers int, vers long, vers double. Un argument, un résultat.

Les espaces initiaux sont sautés, un signe facultatif est lu, les chiffres sont consommés, et tout ce qui suit est ignoré. C'est pratique jusqu'à ce que l'entrée soit erronée :

Les trois affichent 0. Il n'y a aucun code de retour, aucun drapeau, aucun moyen de demander « est-ce que cela a marché ? » - et une valeur qui déborde un int est un comportement indéfini plutôt qu'une erreur signalée. Pour un littéral écrit en dur dans un programme jouet, c'est tolérable. Pour tout ce qu'une personne ou un fichier peut fournir, c'est un générateur de bugs silencieux.

Le bon outil : strtol

strtol (string to long) est la même conversion avec deux canaux pour rapporter ce qui s'est passé.

long strtol(const char *text, char **endptr, int base);

endptr est positionné sur le premier caractère que la fonction n'a pas consommé. base est la base numérique - utilisez 10, ou 0 pour laisser le préfixe du littéral décider (0x pour l'hexadécimal, un zéro initial pour l'octal).

Lire endptr répond aux questions qu'atoi ne peut pas :

  • end == input signifie que rien n'a été converti - le texte ne commençait pas par un nombre.
  • *end != '\0' signifie qu'il restait du contenu. Que ce soit une erreur, c'est votre décision : c'est acceptable en analysant "42 pommes", et problématique quand toute la ligne aurait dû être un nombre.

Le débordement est signalé séparément via errno. L'idiome complet et correct mérite d'être écrit une fois puis réutilisé :

Trois détails rendent cela correct. errno = 0 avant l'appel compte parce que la bibliothèque ne fait que positionner errno ; elle ne l'efface jamais, donc une valeur périmée d'un appel antérieur serait lue comme l'échec de celui-ci. strtol renvoie un long, qui peut être plus large qu'un int, donc le contrôle de plage contre INT_MIN/INT_MAX est distinct d'ERANGE. Et renvoyer le succès comme état tout en livrant la valeur via un pointeur est la façon dont les fonctions C rapportent « soit une valeur, soit un échec » - la valeur de retour ne peut pas faire les deux travaux.

strtoul est l'équivalent non signé, et strtoll/strtoull gèrent long long.

Les autres bases

La base 16 lit l'hexadécimal ; la base 0 inspecte le préfixe et décide elle-même.

La base 0 est un piège dans un cas précis : un décimal complété par des zéros comme "012" est lu en octal et sort à 10. Si votre entrée est décimale, dites 10 explicitement.

La virgule flottante : strtod

strtod a la même forme pour double, et atof a le même défaut qu'atoi.

strtod accepte aussi la notation scientifique ("1.5e3"), les flottants hexadécimaux, et les mots "inf" et "nan". La locale affecte le séparateur décimal, ce qui surprend les gens analysant des fichiers écrits ailleurs - dans la locale « C » par défaut, le séparateur est toujours un point.

Des nombres vers le texte : snprintf

Dans l'autre sens, snprintf est la réponse standard. Il écrit au plus la taille que vous lui donnez, termine toujours la chaîne, et renvoie la longueur dont le résultat complet aurait eu besoin.

La valeur de retour est le test de troncature. Si elle est supérieure ou égale à la taille du tampon, le texte n'a pas tenu :

Deux choses à ne pas prendre : itoa ne fait pas partie du C standard, donc du code l'utilisant ne compilera pas partout ; et sprintf est snprintf sans la limite de taille, ce qui signifie qu'il écrira au-delà de la fin de votre tampon sans broncher.

Les caractères isolés et '0'

Les caractères '0' à '9' sont garantis consécutifs, une simple soustraction convertit donc entre un caractère de chiffre et sa valeur.

Seuls les chiffres se comportent ainsi - les lettres ne sont pas tenues d'être consécutives, donc c - 'a' n'est pas portable pour l'arithmétique alphabétique. Protégez la soustraction avec isdigit avant de lui faire confiance :

Le cast en unsigned char est requis pour la même raison que dans les chaînes : les fonctions de <ctype.h> sont indéfinies pour des arguments négatifs, et un char simple peut être signé.

Quelle fonction utiliser

  • Texte venant d'un utilisateur, d'un fichier ou d'argvstrtol / strtod avec la vérification complète.
  • Un littéral que vous avez écrit et contrôlez → atoi est acceptable, quoique sans intérêt.
  • Un nombre vers un tampon → snprintf, et vérifiez la valeur de retour.
  • Un caractère de chiffre → soustrayez '0', après isdigit.
  • Jamais → gets, sprintf, itoa.

Questions fréquentes

Comment convertit-on une chaîne en int en C ?

strtol(text, &end, 10) est la bonne réponse : il renvoie la valeur, positionne end sur le premier caractère non consommé, et signale le débordement via errno. atoi(text) est plus court mais renvoie 0 aussi bien pour "0" que pour "bonjour", sans moyen de distinguer les deux.

Qu'est-ce qui ne va pas avec atoi en C ?

Il ne peut pas signaler d'échec. atoi("abc") renvoie 0, indiscernable d'un vrai zéro, et le débordement est un comportement indéfini plutôt qu'une erreur signalée. Utilisez strtol dès que le texte vient d'un utilisateur, d'un fichier ou d'un argument de ligne de commande.

Comment convertir un int en chaîne en C ?

snprintf(buf, sizeof buf, "%d", n). C'est standard, borné par la taille du tampon, et cela renvoie le nombre de caractères dont le résultat complet avait besoin - donc une valeur de retour égale ou supérieure à sizeof buf vous dit que le texte a été tronqué. itoa n'est pas du C standard.

Comment transformer le caractère '7' en le nombre 7 en C ?

Soustrayez '0' : int digit = c - '0';. Les caractères de chiffres sont consécutifs dans tout jeu de caractères C, la soustraction donne donc 0 à 9. Protégez-la par isdigit((unsigned char)c) d'abord, car l'arithmétique n'a aucun sens pour autre chose.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER