Quel est le résultat de `strtod (“ 3ex ”, & end)` supposé être? Qu’en est-il de `sscanf`?

Dans mes expériences cette expression

double d = strtod("3ex", &end); 

initialise d avec 3.0 et place le pointeur de end au caractère 'e' dans la chaîne d’entrée. C’est exactement comme je l’attendrais. Le caractère 'e' peut ressembler au début de la partie exposant, mais étant donné que la valeur de l’exposant réel (requirejse par 6.4.4.2) est manquante, ce 'e' doit être traité comme un caractère complètement indépendant.

Cependant, quand je fais

 double d; char c; sscanf("3ex", "%lf%c", &d, &c); 

Je remarque que sscanf consum à la fois '3' et 'e' pour le spécificateur de format %lf . La variable d reçoit la valeur 3.0 . La variable c finit avec 'x' dedans. Cela me semble étrange pour deux raisons.

Premièrement, comme la spécification du langage fait référence à strtod lors de la description du comportement du spécificateur de format %f , j’ai intuitivement prévu que %lf traiterait l’entrée de la même manière que strtod (c’est-à-dire choisit la même position que le sharepoint terminaison). Cependant, je sais qu’historiquement, scanf était censé ne renvoyer qu’un seul caractère dans le stream d’entrée. Cela limite la distance à laquelle tout scanf peut effectuer avec un seul caractère. Et l’exemple ci-dessus nécessite au moins deux personnages à l’avance. Donc, supposons que j’accepte le fait que %lf consommé à la fois '3' et 'e' du stream d’entrée.

Mais nous nous heurtons ensuite au deuxième problème. Maintenant, sscanf doit convertir ce "3e" en double . "3e" n’est pas une représentation valide d’une constante à virgule flottante (là encore, conformément à 6.4.4.2, la valeur de l’exposant n’est pas facultative). Je m’attendrais à ce que sscanf traite cette entrée comme étant erronée: termine lors de la conversion de %lf , renvoie 0 et laisse d et c inchangés. Cependant, le sscanf ci-dessus sscanf termine avec succès (en renvoyant 2 ).

Ce comportement est cohérent entre les implémentations GCC et MSVC de la bibliothèque standard.

Ma question est donc la suivante: dans le document standard en langage C, sscanf peut-il se comporter comme décrit ci-dessus, en se référant aux deux points strtod : consumr plus que strtod et convertir avec succès de telles séquences en "3e" ?

En examinant les résultats de mon expérience, je peux probablement “faire de l’ingénierie inverse” du sscanf du sscanf : consumr autant que “bien”, sans jamais reculer, puis simplement passer la séquence consommée à strtod . Ainsi, 'e' est consommé par %lf puis ignoré par strtod . Mais était-ce exactement tout cela dans la spécification du langage?

Je viens de trouver la description ci-dessous sur die.net

Les fonctions strtod (), strtof () et strtold () convertissent la partie initiale de la chaîne pointée par nptr en double, float et long double représentation, respectivement.

La forme attendue de la chaîne (partie initiale de la) chaîne est un espace vide facultatif, comme le reconnaît isspace (3), un signe plus (“+”) ou moins (“-“) facultatif, puis un nombre décimal (i) , ou (ii) un nombre hexadécimal, ou (iii) un infini, ou (iv) un NAN (not-a-number).

Un nombre décimal consiste en une séquence non vide de chiffres décimaux pouvant contenir un caractère de base (point décimal, dépendant de la localisation, généralement ‘.’), Éventuellement suivi d’un exposant décimal. Un exposant décimal se compose d’un ‘E’ ou ‘e’, ​​suivi d’un signe plus ou moins facultatif, suivi d’une séquence non vide de chiffres décimaux et indique une multiplication par une puissance de 10.

Un nombre hexadécimal consiste en un “0x” ou un “0X” suivi d’une séquence non vide de chiffres hexadécimaux pouvant contenir un caractère de base, éventuellement suivi d’un exposant binary. Un exposant binary se compose d’un “P” ou d’un “p”, suivi d’un signe plus ou moins facultatif, suivi d’une séquence non vide de chiffres décimaux, et indique la multiplication par une puissance égale à 2. Au moins un des caractères de base et un exposant binary presence obligatoire.

Un infini est “INF” ou “INFINITY”, sans tenir compte de la casse.

Un NAN est “NAN” (sans tenir compte de la casse) éventuellement suivi de ‘(‘, une séquence de caractères suivie de ‘)’. La chaîne de caractères spécifie de manière dépendante de l’implémentation le type de NAN.

Puis j’ai effectué une expérience, j’ai exécuté le code ci-dessous avec gcc

 #include  #include  char head[1024], *tail; void core(const char *stmt){ sprintf(head, "%s", stmt); double d=strtod(head, &tail); printf("cover %s to %.2f with length=%ld.\n", head, d, tail-head); } int main(){ core("3.0x"); core("3e"); core("3ex"); core("3e0x"); return 0; } 

et obtenir le résultat

 cover 3.0x to 3.00 with length=3. cover 3e to 3.00 with length=1. cover 3ex to 3.00 with length=1. cover 3e0x to 3.00 with length=3. 

Donc, il semble qu’il devrait y avoir quelques chiffres derrière “e”.

Pour sscanf , j’ai effectué une autre expérience avec le code gcc:

 #include  #include  char head[1024]; void core(const char *stmt){ int i;sscanf(stmt, "%x%s", &i, head); printf("sscanf %s catch %d with '%s'.\n", stmt, i, head); } int main(){ core("0"); core("0x0g"); core("0x1g"); core("0xg"); return 0; } 

puis obtenez le résultat ci-dessous:

 sscanf 0 catch 0 with ''. sscanf 0x0g catch 0 with 'g'. sscanf 0x1g catch 1 with 'g'. sscanf 0xg catch 0 with 'g'. 

Il semble que sscanf essaierait de capturer plus de caractère et de ne pas revenir en arrière s’il était jugé légal (il peut être illégal si la situation est incomplète).