2014-10-13 9 views
10

Bei meinen Versuchen dieser AusdruckWas ist das Ergebnis von `strtod (" 3ex ", & end)? Was ist mit `Sscanf`?

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

initialisiert d mit 3.0 und legt end Zeiger auf 'e' Zeichen in der Eingabezeichenfolge. Das ist genau so, wie ich es erwarten würde. Das Zeichen 'e' könnte als Anfang des Exponententeils aussehen, aber da der tatsächliche Exponentenwert (erforderlich nach 6.4.4.2) fehlt, sollte 'e' als vollständig unabhängiges Zeichen behandelt werden.

Allerdings, wenn ich

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

tun merke ich, dass sscanf sowohl verbraucht '3' und 'e' für die %lf Formatbezeichner. Variable d empfängt 3.0 Wert. Variable c endet mit 'x' darin. Das sieht aus zwei Gründen komisch aus.

Erstens, da die Sprachspezifikation zu strtod bezieht, wenn das Verhalten von %f Formatbezeich beschreiben, I erwarteten intuitiv %lf die Eingabe der gleiche Weise strtod tut (das heißt wählen, die gleiche Position wie der Abschlusspunkt) zu behandeln. Allerdings weiß ich, dass scanf historisch nicht mehr als ein Zeichen zurück in den Eingabestream zurückgeben sollte. Das begrenzt die Entfernung eines beliebigen Look-Ahead scanf um ein Zeichen. Und das obige Beispiel erfordert mindestens zwei Zeichen Vorausschau. Nehmen wir an, ich akzeptiere die Tatsache, dass %lf sowohl '3' als auch 'e' aus dem Eingabestrom verbraucht hat.

Aber dann laufen wir in die zweite Ausgabe. Jetzt muss sscanf das "3e" in den Typ double konvertieren. "3e" ist keine gültige Darstellung einer Gleitkommakonstante (auch hier ist der Exponentenwert gemäß 6.4.4.2 nicht optional). Ich würde erwarten, sscanf diesen Eingang als fehlerhaft zu behandeln: Beenden Sie während der %lf Konvertierung, geben Sie 0 zurück und lassen Sie d und c unverändert. Das obige sscanf wird jedoch erfolgreich abgeschlossen (2 wird zurückgegeben).

Dieses Verhalten ist konsistent zwischen GCC- und MSVC-Implementierungen der Standardbibliothek.

Also, meine Frage ist, wo genau in der Sprache C Standard-Dokument erlaubt es sscanf wie oben beschrieben zu verhalten, zu den beiden oben genannten Punkte beziehen: Verbrauch von mehr als strtod tut und erfolgreich solche Sequenzen als "3e" Umwandlung?

Wenn ich mir meine Testergebnisse anschaue, kann ich wahrscheinlich das Verhalten von sscanf "reverse engineeren": konsumiere so viel wie "sieht richtig aus" gehe nie zurück und gebe dann die verbrauchte Sequenz einfach an strtod weiter. Auf diese Weise wird 'e' von %lf verbraucht und dann einfach von strtod ignoriert. Aber war genau das alles in der Sprachspezifikation?

+0

Vielleicht liegt der Grund (obwohl keine gute Ausrede) für den Unterschied in der Tatsache, dass "sscanf" in "stdio" und "strtod" in "stdlib" steht. –

+0

Nicht wirklich sicher, ich verstehe: Warum scheint das Ergebnis von sscanf Ihnen fremd zu sein? Was genau hast du erwartet? Könnten Sie bitte ein bisschen mehr Details geben? – HighPredator

+2

@HighPredator: OP bedeutet wahrscheinlich, dass die Variable "c" den Wert "e" und nicht den Wert "x" erreichen soll. Oder vielleicht sollte es überhaupt keinen Wert erreichen, und die Funktion 'sscanf' sollte 1 statt 2 zurückgeben (so emuliert es genau das Verhalten von' strtod'). –

Antwort

1

ich die Beschreibung nur auf die.net

Die strtod(), strtof unten finden() und strtold() Funktionen konvertieren die anfängliche Teil des Strings von nptr wies auf das Doppelte, float und lang doppelte Darstellung jeweils.

Die erwartete Form des (Anfangsteils des) String ist optional Leerraum führt, wie durch isspace Erkannt (3), ein optional zuzüglich (‚+‘) oder Minuszeichen (‚-‘) und dann entweder (i) eine Dezimalzahl oder (ii) eine Hexadezimalzahl oder (iii) ein Unendlich oder (iv) eine NAN (Nicht-eine-Nummer).

Dezimalzahl besteht aus einer nicht-leeren Folge von Dezimalziffern möglicherweise ein Dezimalzeichen (Dezimalpunkt, locale-abhängige, in der Regel ‚‘) enthält, die gegebenenfalls durch einen dezimalen Exponenten folgte. Ein Dezimalwert Exponent besteht aus einem ‚E‘ oder ‚e‘, durch ein optional Plus- oder Minuszeichen, gefolgt von einer nicht-leeren Folge von Dezimalziffern gefolgt, und zeigt eine Multiplikation mit einer Potenz von 10

Eine Hexadezimalzahl besteht aus einem "0x" oder "0X", gefolgt von einer nicht leeren Folge von hexadezimalen Ziffern, die möglicherweise ein Radixzeichen enthalten, , optional gefolgt von einem binären Exponenten. Ein binärer Exponent besteht aus eines 'P' oder 'p', gefolgt von einem optionalen Plus- oder Minuszeichen, gefolgt von durch eine nichtleere Folge von Dezimalziffern, und zeigt Multiplikation mit einer Potenz von 2. Mindestens eines von Radixzeichen und binärer Exponent muss vorhanden sein.

Eine Unendlichkeit ist entweder "INF" oder "INFINITY", ungeachtet des Falls.

Eine NAN ist "NAN" (Groß-/Kleinschreibung wird ignoriert), optional gefolgt von "(', einer Zeichenfolge, gefolgt von')". Die Zeichenkette gibt in einer implementierungsabhängigen Weise den Typ von NAN an.

Dann habe ich ein Experiment durchgeführt, ausgeführt ich den Code unten mit gcc

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

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; 
} 

und erhalten das Ergebnis

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. 

So scheint es, dass es einige Stellen hinter ‚e sein ".

Für sscanf, führte ich ein weiteres Experiment mit gcc Code:

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

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; 
} 

dann unter die Ausgabe erhalten:

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

Es scheint, dass sscanf versuchen würde, mehr Charakter zu fangen und würde nicht zurückrollen Wenn ES BEURTEILT, DASS ES ZUR ZEIT RECHTLICH IST (KANN BEI UNVERMEIDENDER SITUATION RECHTSWIDRIG SEIN).