Lire un entier dans argv en C avec strtol
Lis un entier passé dans argv sans accepter 12px, un dépassement ou une valeur hors de la plage voulue, avec strtol et errno.
Ton programme attend un nombre avec ./programme 42. Puis quelqu'un lance ./programme 12px, oublie l'argument ou passe un entier trop grand. atoi te laisse deviner ce qui s'est passé. strtol te donne les informations pour décider.
En bref
Pour lire un argument entier en C, appelle strtol(argv[1], &end, 10) après avoir mis errno à zéro. Refuse l'entrée si end == argv[1], si *end != '\0', si errno == ERANGE, ou si la valeur sort des bornes de ton programme. Vérifie aussi argc avant tout accès à argv[1].
Quel est le programme complet pour lire ./programme 42 ?
Ce programme attend exactement un argument, accepte un entier décimal de 0 à 100000, puis l'affiche. La borne n'est pas une propriété de strtol : c'est une règle du programme, placée juste après la conversion.
#include <errno.h>#include <stdio.h>#include <stdlib.h>
static int parse_count(const char *text, long *count){ char *end; long value;
errno = 0; value = strtol(text, &end, 10); if (end == text || *end != '\0' || errno == ERANGE) return 0; if (value < 0 || value > 100000) return 0; *count = value; return 1;}
int main(int argc, char *argv[]){ long count;
if (argc != 2) { fprintf(stderr, "usage: %s COUNT\n", argv[0]); return 2; } if (!parse_count(argv[1], &count)) { fprintf(stderr, "invalid count: %s\n", argv[1]); return 1; } printf("count=%ld\n", count); return 0;}$ gcc -std=c17 -Wall -Wextra -Werror parse-argv.c -o parse-argv$ ./parse-argv 42count=42$ ./parse-argv 12px; echo status=$?invalid count: 12pxstatus=1$ ./parse-argv 999999999999999999999999; echo status=$?invalid count: 999999999999999999999999status=1$ ./parse-argv -1; echo status=$?invalid count: -1status=1$ ./parse-argv; echo status=$?usage: ./parse-argv COUNTstatus=2argc compte le nom du programme lui-même. Avec ./parse-argv 42, il vaut donc 2 et argv[1] désigne la chaîne "42". Quand argc vaut 1, lire argv[1] serait déjà une erreur.
- argumentla chaîne passée au programme
- strtollit les chiffres et place end
- end + errnoaucun reste, pas de dépassement
- countla borne 0..100000 est respectée
Pourquoi atoi ne suffit-il pas pour un argument ?
atoi("0") et atoi("bonjour") donnent tous les deux 0 sur les bibliothèques courantes. Son résultat ne dit donc pas si l'utilisateur a demandé zéro ou si aucune conversion n'a eu lieu. Sa fiche de manuel détaille aussi le problème de dépassement.
strtol garde le résultat et un pointeur sur le premier caractère qu'il n'a pas lu. Avec 12px, value vaut 12 mais end pointe sur p. Tester *end != '\0' transforme ce reste en refus. C'est ce test qui évite de prendre 12px pour 12.
int count = atoi(argv[1]);char *end;long count = strtol(argv[1], &end, 10);if (end == argv[1] || *end != '\0') return 1;atoi ne permet pas non plus de distinguer un dépassement. La conversion avec strtol renvoie alors LONG_MAX ou LONG_MIN et positionne errno à ERANGE. Mets errno = 0 juste avant l'appel : une fonction réussie ne le remet pas forcément à zéro.
Dans quel ordre vérifier end et errno ?
L'ordre du programme est volontaire. end == text répond à « aucun chiffre ? ». errno == ERANGE répond à « le nombre tient-il dans un long ? ». *end != '\0' répond à « la chaîne entière était-elle un nombre ? ». Les comparaisons finales expriment les règles du programme, ici une quantité positive bornée.
strtol accepte des blancs au début. Pour un argument de ligne de commande, c'est rarement gênant parce que le shell sépare déjà les arguments. Si ton format doit les interdire, teste le premier caractère avec isspace((unsigned char)text[0]) avant l'appel.
Le code choisit la base 10. La valeur 0 demande à strtol de deviner une base à partir du préfixe : 0x pour l'hexadécimal et, selon la bibliothèque et le standard visé, d'autres préfixes peuvent compter. Pour une quantité que l'utilisateur doit taper en décimal, 10 évite cette ambiguïté.
strtol
Écris gm_strtol, ta version de la fonction. L'exercice te fait avancer le pointeur de fin et gérer les bases, exactement les deux informations que le programme utilise pour décider.
Puis-je stocker le résultat dans un int ?
Oui, mais ne fais la conversion vers int qu'après une comparaison avec INT_MIN et INT_MAX. strtol promet que le résultat tient dans un long, pas dans tous les types plus petits. Si ton programme doit accepter un entier non signé ou une plage plus large, choisis la fonction adaptée, comme strtoul ou strtoll, et conserve les mêmes vérifications.
Pour une réponse lue au clavier, la méthode est la même après avoir lu une ligne complète. L'article scanf en C : pourquoi fgets saute une ligne montre ce cas et explique pourquoi il vaut mieux séparer lecture et conversion.
Questions fréquentes
Pourquoi strtol renvoie-t-il 0 avec abc ?
Quand aucun chiffre ne peut être lu, strtol renvoie 0 et place end au début de la chaîne. La valeur seule reste ambiguë : compare end avec l'argument d'origine.
Est-ce que 12 suivi d'un espace est accepté ?
Pas par ce programme, car l'espace reste sous *end. Tu peux autoriser des blancs de fin en les sautant avec isspace avant de tester *end, mais fais-en une règle explicite.
Pourquoi ne pas utiliser sscanf ?
sscanf peut lire un entier, mais strtol rend la position d'arrêt et le dépassement faciles à contrôler pour une chaîne entière. Ici, c'est précisément ce qu'il faut vérifier.
Dois-je appeler strtol avec base 0 ?
Non pour un compteur décimal. Passe 10 quand 08 doit signifier huit et que 0x10 doit être refusé. Choisis 0 seulement si ton interface documente les préfixes qu'elle accepte.
Sources
