Menu

Da stringa a int in C: atoi, strtol e convertire numeri in testo

Convertire tra testo e numeri in C: perché atoi non può segnalare un errore, come usare correttamente strtol e strtod con endptr ed errno, trasformare numeri in stringhe con snprintf e i trucchi sulle singole cifre con '0'.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Il testo arriva da utenti, file e riga di comando sotto forma di caratteri. "42" sono tre byte, '4', '2' e '\0', e nulla lì dentro è il numero 42 finché non lo converti. Il C offre due generazioni di funzioni di conversione: quelle vecchie e brevi, che non sanno segnalare errori, e quelle più lunghe, che invece lo sanno fare. Questa pagina le mostra entrambe e spiega perché i programmi veri usano il secondo gruppo.

La vecchia famiglia: atoi, atol, atof

Si leggono come i loro nomi: da ASCII a int, a long, a double. Un argomento, un risultato.

Gli spazi iniziali vengono saltati, si legge un segno facoltativo, si consumano le cifre e tutto ciò che segue viene ignorato. È comodo finché l'input non è sbagliato:

Tutti e tre stampano 0. Non c'è un codice di ritorno, né un flag, né un modo per chiedere "ha funzionato?", e un valore che supera la capacità di un int è comportamento indefinito invece di un errore segnalato. Per un letterale scritto a mano in un programma giocattolo si può tollerare. Per tutto ciò che può fornire una persona o un file, è una fabbrica di bug silenziosi.

Lo strumento giusto: strtol

strtol (string to long) è la stessa conversione con due canali per segnalare cosa è successo.

long strtol(const char *text, char **endptr, int base);

endptr viene impostato sul primo carattere che la funzione non ha consumato. base è la base numerica: usa 10, oppure 0 per lasciar decidere al prefisso del letterale (0x per l'esadecimale, uno 0 iniziale per l'ottale).

Leggere endptr risponde alle domande a cui atoi non può rispondere:

  • end == input significa che non è stato convertito nulla: il testo non iniziava con un numero.
  • *end != '\0' significa che c'era del contenuto in coda. Se sia un errore lo decidi tu: va bene quando analizzi "42 mele", è un problema quando l'intera riga doveva essere un numero.

L'overflow viene segnalato a parte tramite errno. L'idioma completo e corretto vale la pena di scriverlo una volta e riusarlo:

Tre dettagli lo rendono corretto. errno = 0 prima della chiamata è importante perché la libreria imposta errno ma non lo azzera mai, quindi un valore rimasto da una chiamata precedente verrebbe letto come errore di questa. strtol restituisce un long, che può essere più largo di un int, quindi il controllo dell'intervallo con INT_MIN/INT_MAX è separato da ERANGE. E restituire il successo come stato consegnando il valore tramite un puntatore è il modo in cui le funzioni C comunicano "un valore oppure un errore": il valore di ritorno non può fare entrambe le cose.

strtoul è la controparte senza segno, e strtoll/strtoull gestiscono i long long.

Altre basi

La base 16 legge l'esadecimale; la base 0 esamina il prefisso e decide da sola.

La base 0 è una trappola in un caso preciso: un decimale con zeri iniziali come "012" viene letto come ottale e diventa 10. Se il tuo input è decimale, scrivi 10 esplicitamente.

Virgola mobile: strtod

strtod ha la stessa forma per i double, e atof ha lo stesso difetto di atoi.

strtod accetta anche la notazione scientifica ("1.5e3"), i float esadecimali e le parole "inf" e "nan". Il locale influenza il separatore decimale, e questo sorprende chi analizza file scritti altrove: nel locale predefinito "C" il separatore è sempre il punto.

Da numeri a testo: snprintf

Nella direzione opposta, snprintf è la risposta standard. Scrive al massimo la dimensione che le dai, termina sempre la stringa e restituisce la lunghezza che sarebbe servita per il risultato completo.

Il valore di ritorno è il controllo del troncamento. Se è maggiore o uguale alla dimensione del buffer, il testo non ci stava:

Due cose da non usare: itoa non fa parte del C standard, quindi il codice che la usa non compila ovunque; e sprintf è snprintf senza il limite di dimensione, il che significa che scriverà oltre la fine del tuo buffer senza lamentarsi.

Singoli caratteri e '0'

I caratteri delle cifre da '0' a '9' sono garantiti consecutivi, quindi una semplice sottrazione converte tra un carattere cifra e il suo valore.

Solo le cifre si comportano così: le lettere non devono per forza essere consecutive, quindi c - 'a' non è portabile per fare aritmetica sull'alfabeto. Proteggi la sottrazione con isdigit prima di fidarti:

Il cast a unsigned char è necessario per lo stesso motivo visto per le stringhe: le funzioni di <ctype.h> sono indefinite per argomenti negativi, e un char semplice può avere il segno.

Quale funzione usare

  • Testo da un utente, da un file o da argv → strtol / strtod con il controllo completo.
  • Un letterale scritto e controllato da te → atoi è accettabile, anche se nulla di più.
  • Un numero dentro un buffer → snprintf, controllando il valore di ritorno.
  • Un singolo carattere cifra → sottrai '0', dopo isdigit.
  • Mai → gets, sprintf, itoa.

Domande frequenti

Come si converte una stringa in int in C?

strtol(text, &end, 10) è la risposta corretta: restituisce il valore, imposta end sul primo carattere che non ha consumato e segnala l'overflow tramite errno. atoi(text) è più breve, ma restituisce 0 sia per "0" sia per "ciao", senza alcun modo di distinguere i due casi.

Cosa c'è di sbagliato in atoi in C?

Non può segnalare un errore. atoi("abc") restituisce 0, indistinguibile da uno zero vero, e l'overflow è comportamento indefinito invece di un errore segnalato. Usa strtol ogni volta che il testo arriva da un utente, da un file o da un argomento della riga di comando.

Come si converte un int in stringa in C?

snprintf(buf, sizeof buf, "%d", n). È standard, limitato dalla dimensione del buffer e restituisce il numero di caratteri che servivano per il risultato completo, quindi un valore di ritorno uguale o superiore a sizeof buf ti dice che il testo è stato troncato. itoa non fa parte del C standard.

Come si trasforma il carattere '7' nel numero 7 in C?

Sottrai '0': int digit = c - '0';. I caratteri delle cifre sono consecutivi in ogni set di caratteri del C, quindi la sottrazione dà un valore da 0 a 9. Proteggila prima con isdigit((unsigned char)c), perché per qualsiasi altro carattere il calcolo non ha senso.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA