Les fonctions standard de la bibliothèque C strtof
et strtod
ont les signatures suivantes:
float strtof(const char *str, char **endptr); double strtod(const char *str, char **endptr);
Ils décomposent chacun la chaîne d’entrée, str
, en trois parties:
Si endptr
n’est pas NULL
, alors *endptr
est défini sur un pointeur sur le caractère qui suit immédiatement le dernier caractère faisant partie de la conversion (en d’autres termes, le début de la séquence de fin).
Je me demande: pourquoi endptr
est- endptr
alors un pointeur sur un pointeur non const
? *endptr
n’est-il pas un pointeur dans une chaîne de caractère const
(la chaîne d’entrée str
)?
La raison est simplement la facilité d’utilisation. char *
peut convertir automatiquement en const char *
, mais char **
ne peut pas convertir automatiquement en const char **
, et le type réel du pointeur (dont l’adresse est passée) utilisé par la fonction appelante est beaucoup plus susceptible d’être char *
que const char *
. La raison pour laquelle cette conversion automatique n’est pas possible est qu’il existe un moyen non évident de l’utiliser pour supprimer la qualification de const
en plusieurs étapes, chaque étape semblant parfaitement valide et correcte en soi. Steve Jessop a fourni un exemple dans les commentaires:
si vous pouviez convertir automatiquement
char**
enconst char**
, alors vous pourriez le fairechar *p; char **pp = &p; const char** cp = pp; *cp = (const char*) "hello"; *p = 'j';.
Pour la sécurité constante, l’une de ces lignes doit être illégale et, comme les autres sont des opérations parfaitement normales,
cp = pp;
Une meilleure approche aurait été de définir ces fonctions pour prendre void *
à la place de char **
.Les caractères (Le texte endommagé était en fait une très mauvaise idée; non seulement il empêche toute vérification de type, mais C interdit en fait les objects de type char **
et const char **
peuvent tous deux être convertis automatiquement en void *
.char *
et const char *
à alias.) Sinon, ces fonctions auraient pu prendre ptrdiff_t *
ou size_t *
argument dans lequel stocker le décalage de la fin, plutôt qu’un pointeur sur celle-ci. C’est souvent plus utile de toute façon.
Si vous aimez cette dernière approche, n’hésitez pas à écrire un tel wrapper autour des fonctions de bibliothèque standard et à appeler votre wrapper, de manière à garder le rest de votre code propre et sans casting.
Facilité d’utilisation. L’argument str
est marqué comme const
car l’argument d’entrée ne sera pas modifié. Si endptr
était const
, cela indiquerait à l’ appelant qu’il ne devrait pas modifier les données référencées depuis endptr
en sortie, mais c’est souvent ce que l’appelant souhaite faire. Par exemple, je souhaiterais peut-être mettre fin à une chaîne après l’avoir extraite du flottant:
float StrToFAndTerminate(char *Text) { float Num; Num = strtof(Text, &Text); *Text = '\0'; return Num; }
Chose parfaitement raisonnable à vouloir faire, dans certaines circonstances. Ne fonctionne pas si endptr
est de type const char **
.
Idéalement, endptr
devrait être endptr
entrée réelle de str
, mais C ne fournit aucun moyen de l’indiquer par le biais de sa syntaxe. ( Anders Hejlsberg parle de cela en décrivant pourquoi const
été laissé en C #.)