2014-10-13 26 views
10

Nei miei esperimenti questa espressioneQual è il risultato di `strtod (" 3ex ", & end)` dovrebbe essere? Che dire di `sscanf`?

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

inizializza d con 3.0 e pone end puntatore 'e' carattere nella stringa di input. Questo è esattamente come mi aspetto che si comporti. Il carattere 'e' potrebbe sembrare un inizio della parte esponente, ma poiché manca il valore di esponente effettivo (richiesto dalla 6.4.4.2), tale 'e' deve essere considerato come un carattere completamente indipendente.

Tuttavia, quando lo faccio

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

mi accorgo che sscanf consuma sia '3' e 'e' per il formato %lf specificatore. La variabile d riceve il valore 3.0. La variabile c termina con 'x'. Questo mi sembra strano per due ragioni.

In primo luogo, dato che la specifica del linguaggio riferisce a strtod nel descrivere il comportamento di %f di formato, intuitivamente aspettato %lf per trattare l'ingresso nello stesso modo strtod fa (cioè scegliere la stessa posizione del punto terminale). Tuttavia, so che storicamente lo scanf doveva restituire non più di un carattere al flusso di input. Ciò limita la distanza di qualsiasi look ahead scanf può essere eseguito da un solo carattere. E l'esempio sopra richiede almeno due caratteri di previsione. Quindi, supponiamo che accetti il ​​fatto che lo %lf abbia consumato sia '3' sia 'e' dal flusso di input.

Ma poi ci imbattiamo nel secondo numero. Ora sscanf deve convertire quello "3e" nel tipo double. "3e" non è una rappresentazione valida di una costante in virgola mobile (anche in questo caso, secondo 6.4.4.2 il valore dell'esponente non è facoltativo). Mi aspetto che sscanf consideri errato questo input: termina durante la conversione %lf, restituisce 0 e lascia d e c invariato. Tuttavia, il precedente sscanf viene completato correttamente (restituendo 2).

Questo comportamento è coerente tra le implementazioni GCC e MSVC della libreria standard.

Così, la mia domanda è: dove esattamente nel linguaggio C standard di documento non si permette sscanf a comportarsi come sopra descritto, facendo riferimento a questi due punti: che consumano più di strtod fa e con successo la conversione di tali sequenze come "3e"?

Osservando i risultati del mio esperimento probabilmente posso "decodificare" il comportamento dello sscanf: consumare quanto "sembra giusto" senza mai tornare indietro e quindi passare semplicemente la sequenza consumata a strtod. In questo modo, 'e' viene consumato da %lf e quindi viene semplicemente ignorato da strtod. Ma lo erano esattamente le specifiche del linguaggio?

+0

Forse la ragione (sebbene non una scusa molto buona) della differenza sta nel fatto che 'sscanf' è in' stdio' e 'strtod' è in' stdlib'. –

+0

Non sono sicuro di aver capito: perché il risultato di sscanf ti sembra strano? Cosa ti aspettavi esattamente? Potresti fornire qualche dettaglio in più? – HighPredator

+2

@HighPredator: OP probabilmente significa che la variabile 'c' dovrebbe raggiungere il valore' 'e'' e non il valore '' x''. O forse non dovrebbe raggiungere alcun valore, e la funzione 'sscanf' dovrebbe restituire 1 invece di 2 (quindi emula esattamente il comportamento di' strtod'). –

risposta

1

Ho appena trovato la descrizione qui sotto in die.net

Lo strtod(), strtof(), e strtold() funzioni convertono il porzione iniziale della stringa puntata da nptr raddoppiare, galleggiante, e lungo doppia rappresentazione, rispettivamente.

La forma atteso della (parte iniziale del) stringa è facoltativa spazi bianchi come riconosciuto dalla isspace (3), un optional più ('+') o un segno meno ('-') e poi o (i) un numero decimale, o (ii) un numero esadecimale o (iii) un infinito, o (iv) un NAN (non-un-numero).

Un numero decimale è costituito da una sequenza non vuota di cifre decimali eventualmente contenenti un carattere radice (punto decimale, dipendente dal luogo, solitamente ''), eventualmente seguita da un numero decimale esponente. Un esponente decimale è costituito da un 'E' o 'e', ​​seguito da un segno più o meno opzionale, seguito da una sequenza non vuota di cifre decimali, e indica la moltiplicazione per una potenza di 10.

Un numero esadecimale consiste di un "0x" o "0X" seguito da una sequenza non vuota di cifre esadecimali possibilmente contenente un carattere di radice, opzionalmente seguita da un esponente binario. Un esponente binario è costituito da di una 'P' o 'p', seguito da un segno più o meno facoltativo, seguito da da una sequenza non vuota di cifre decimali e indica la moltiplicazione da una potenza di 2. Almeno uno di carattere radix e l'esponente binario deve essere presente.

Un infinito è "INF" o "INFINITY", trascurando il caso.

Un NAN è "NAN" (trascurando il caso) opzionalmente seguito da '(', una sequenza di caratteri seguita da ')'. La stringa di caratteri specifica in modo dipendente dall'implementazione il tipo di NAN.

Poi ho eseguito un esperimento, ho eseguito il codice qui sotto con 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; 
} 

e ottenere il risultato

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. 

Così, sembra che non ci dovrebbero essere alcuni cifre dietro 'e '.

Per sscanf, ho eseguito un altro esperimento con il codice di gcc:

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

quindi ottenere l'output di seguito:

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

Sembra che sscanf sarebbe cercare di catturare più carattere e non sarebbe di rollback se HA GIUDICATO È LEGALMENTE ATTUALE (PU BE ESSERE ILLEGALE CON SITUAZIONE INCOMPLETA).