Technopedia Center
PMB University Brochure
Faculty of Engineering and Computer Science
S1 Informatics S1 Information Systems S1 Information Technology S1 Computer Engineering S1 Electrical Engineering S1 Civil Engineering

faculty of Economics and Business
S1 Management S1 Accountancy

Faculty of Letters and Educational Sciences
S1 English literature S1 English language education S1 Mathematics education S1 Sports Education
teknopedia

teknopedia

teknopedia

teknopedia

teknopedia

teknopedia
teknopedia
teknopedia
teknopedia
teknopedia
teknopedia
  • Registerasi
  • Brosur UTI
  • Kip Scholarship Information
  • Performance
  1. Weltenzyklopädie
  2. scanf - Teknopedia
scanf - Teknopedia
Abbozzo
Questa voce sull'argomento programmazione è solo un abbozzo.
Contribuisci a migliorarla secondo le convenzioni di Teknopedia.

scanf (abbreviazione dell'inglese scan formatted) è una funzione che viene implementata in diversi linguaggi di programmazione. Viene utilizzata per l'inserimento di dati formattati (caratteri e numeri). Da essa derivano inoltre altre funzioni, tra le cui più citate fscanf e sscanf.

Ha origine dal linguaggio C dove è inclusa nella libreria stdio.h, ma queste funzioni sono precedenti a C[1] e possiedono altri nomi, come ad esempio readf in ALGOL 68.

Storia

[modifica | modifica wikitesto]

La funzione è stata implementata da Mike Lesk[2], assieme al resto della libreria cui appartiene, la quale è stata poi ufficialmente resa parte di Unix dalla versione 7 (1979).

Utilizzo

[modifica | modifica wikitesto]

La funzione ha il seguente prototipo:

int scanf(const char *format, ...);

Dal C99 e a seguire è stato introdotto restrict[3] con i definitori (vedi anche restrict):

int scanf(const char * restrict format, ...);

La funzione legge dei caratteri dal canale standard di input (stdin[4]), solitamente la tastiera, li converte secondo le specifiche di formattazione fornite dall'argomento format e memorizza i valori ottenuti negli argomenti seguenti in concordanza del tipo di dato richiesto.[5] La funzione si ferma quando esaurisce la propria stringa di formattazione o quando alcuni dati in ingresso non soddisfano le specifiche di conversione. Il valore restituito è un numero intero che indica quanti valori sono stati estratti correttamente: negativo se si è verificato un errore, positivo se c'è stata una lettura e in particolare 0 (zero) se non è stata fatta alcuna assegnazione perché il primo carattere letto non è conforme alla prima specifica della stringa di formato.

Similarmente alla funzione printf, il ... sta ad indicare che si tratti di una funzione variadica (che può assumere un numero variabile di parametri).

Sintassi

[modifica | modifica wikitesto]

La sintassi generica da seguire per chiamare la funzione è:

scanf (tipo, &arg);

Da notare come si debba fornire l'indirizzo di memoria di arg (tramite l'operatore unario &) in cui verranno memorizzati i dati. A ciò fa però eccezione il definitore %s perché, in quanto array di caratteri, è già un puntatore: nella dichiarazione di un array infatti, viene memorizzato l'indirizzo di memoria del primo elemento che lo compone, pertanto, dato ad esempio un vettore strvet[37], le due espressioni *strvet e strvet[0] sono equivalenti.[6]

Qui di seguito è riportato un esempio in C

#include <stdio.h>   
/* Libreria standard per l'I/O */

int main() {
    int n;
    printf("Inserisci il valore di N: ");
    scanf("%d", &n);
    printf("N al quadrato è uguale a %d\n", n*n);
    return 0;    
}

Nell'esempio proposto viene richiesto all'utente un numero intero e viene calcolato il quadrato di tale numero.

La funzione scanf ha lo scopo di rilevare quanto immesso dall'utente tramite la tastiera, convertirlo in un numero intero e memorizzarne il risultato nella variabile n.

La lettura si blocca appena incontra un carattere non conforme a quello definito dal definitore di formato.

Quindi, in questo esempio, fornendo un input del tipo 435 9483 9342 oppure 435ute, si otterrebbe in entrambi i casi 435*435 come output (a schermo), lasciando nel buffer di memoria il resto della stringa, che potrebbe essere letta da un'altra funzione o dalla stessa, se inserita in un ciclo apposito. Bisogna pertanto prestare attenzione a questo fattore perché potrebbe creare incongruenze con le altre funzioni che accederebbero successivamente al buffer.

L'argomento format

[modifica | modifica wikitesto]

L'argomento format è una stringa che specifica l'interpretazione dell'input e può contenere una o più di:

  • Spazi vuoti: vuoto , tabulazioni \t o nuova riga \n e \r e anche le rispettive traduzioni numeriche ASCII, compreso lo \0 (zero di fine stringa). Un carattere di spazio vuoto comporta che n legga, ma non memorizzi, tutti i caratteri di spazio vuoti consecutivi nell'input fino al successivo carattere diverso da uno spazio vuoto;
  • Caratteri diversi da uno spazio vuoto, ad eccezione del segno di percentuale (%). Un carattere diverso da uno spazio vuoto comporta che scanf legga, ma non memorizzi, un carattere diverso da uno spazio vuoto corrispondente. Se il carattere successivo nel flusso di input non corrisponde, scanf termina;
  • Specifiche di formato introdotte dal segno di percentuale (%). Una specifica di formato determina la lettura e la conversione dei caratteri nell'input in un valore di un tipo specificato. Il valore viene assegnato a un argomento nell'elenco degli argomenti.
I definitori di formato (format specifier)
[modifica | modifica wikitesto]

Una specifica di formato presenta la forma seguente[7]:

%[*][ampiezza][{h|l|L}]tipo

Come accade anche nella funzione printf, gli argomenti tra parentesi possono essere omessi a seconda delle esigenze.

La ampiezza è un numero intero positivo che controlla il numero massimo di caratteri da leggere da stdin per quella specifica di formato.

Il carattere di soppressione dell'assegnazione (*) serve a leggere un valore dall'input e a ignorarlo, senza pertanto assegnarlo a una variabile.

Nella tabella seguente sono mostrati i tipi di dato[8] da poter inserire:

Descrizione
c Carattere singolo ASCII (non solo quelli stampabili)
i Numero intero
d Numero intero decimale
u Numero intero decimale senza segno
o Numero intero ottale senza segno
x Numero intero esadecimale senza segno
f Numero reale a singola precisione (float)
a Numero reale (float) in notazione esadecimale (presente dal C99)
e Numero reale in virgola mobile in notazione scientifica [-]d.ddde±dd
g Numero reale in notazione estesa (f) e in notazione scientifica (e)
s Stringa (array di caratteri e, poiché già puntatore, nella scanf non ha bisogno di '&')
p Indirizzo di memoria (puntatore)
[set] Lista di caratteri (si blocca appena incontra un carattere non presente nella lista)
n nessun input letto dal buffer
l Modificatore di lunghezza (long)
L Modificatore di lunghezza (long long)
h Modificatore di lunghezza (short)
% Solo il carattere di percentuale

Una differenza che si ritrova con la funzione printf sta nell'utilizzo dei definitori %i, %d, %u, %o, e %x, in particolare per %i e %d perché sono sinonimi per l'output, ma diversi se usati per l'input. Bisogna pertanto fare attenzione al tipo di numero intero fornito. Mentre %i è in grado di riconoscere una base decimale da una ottale e da una esadecimale (rispettivamente tramite l'uso del prefisso 0 e 0x), in tutti gli altri casi si sta definendo a prescindere una base specifica e un eventuale segno[9].

Il definitore %l (long, cioè lunghezza doppia) viene sempre seguito da altri definitori per indicare che lo spazio di memoria necessario da allocare (fa unica eccezione p perché, in quanto già indirizzo di memoria, non ha bisogno di una ulteriore specifica di formato). Pertanto, ad esempio, %lf indicherà un numero reale a doppia precisione (double), mentre %lu indicherà un numero intero decimale senza segno a 64 bit (invece degli usuali 32 bit).

Il definitore %h (short, cioè mezza lunghezza) si antepone solo per i definitori di numeri interi. Pertanto, ad esempio, %hi indicherà un numero intero a 16 bit.

Il definitore %L si antepone ai numeri reali in virgola mobile (es. %Lf) per indicare i formati long double.

Il definitore %g ha la proprietà di poter leggere i numeri in virgola mobile sia del formato %f, che del formato %e. Proprietà che nella funzione printf si traduce nel poter restituire il formato che si presenta più corto tra i due.

Il definitore %n punta a un tipo di dato int, in cui viene memorizzato il numero di caratteri letti correttamente dallo stream o dal buffer fino a quel punto nella chiamata a scanf().

Il definitore %p ha la particolarità di dover puntare a un puntatore che è stato dichiarato, o per lo meno convertito, come void.

Per l'uso degli insiemi di caratteri ([]), si può complementarizzare tramite l'insieme di caratteri scelti: se il primo carattere nell'insieme è un accento circonflesso (^), l'effetto viene invertito e il campo di input viene letto fino al primo carattere che appartiene nel resto dell'insieme di caratteri (operando a tutti gli effetti l'operazione XOR).

Il definitore %% corrisponde al solo carattere di percentuale (%).[10]

Valore restituito

[modifica | modifica wikitesto]

La funzione scanf() restituisce il numero (int) di campi che sono stati convertiti e assegnati correttamente. Il valore di ritorno non comprende il numero di campi che sono stati letti, ma non assegnati.

Il valore di ritorno è negativo, EOF (end of file), per un tentativo di lettura alla fine del file, se non è stata eseguita alcuna conversione. Un valore di ritorno 0 (zero) indica che non sono stati assegnati campi.[11]

Condizioni di errore

[modifica | modifica wikitesto]

Se il tipo di argomento a cui deve essere assegnato è diverso dalla specifica di formato, possono verificarsi risultati imprevedibili. Ad esempio, la lettura di un valore a virgola mobile, ma l'assegnazione in una variabile di tipo int, non è corretta e avrebbe risultati imprevedibili.

Se ci sono più argomenti che specifiche di formato, gli argomenti supplementari vengono ignorati. I risultati non sono definiti se non vi sono argomenti sufficienti per le specifiche di formato.

Se la stringa di formato contiene una specifica di formato non valida e vengono utilizzate specifiche di formato posizionali, errno verrà impostato su EILSEQ.

Se vengono utilizzate specifiche di formato posizionale e non vi sono argomenti sufficienti, errno verrà impostato su EINVAL.

Se si verifica un errore di conversione, errno può essere impostato su ECONVERT.[12]

Overflow aritmetico

[modifica | modifica wikitesto]

Durante la conversione da stringa a numero (come con atoi, atol e atof) vi è il problema per cui la funzione scanf non riesce a determinare un overflow aritmetico (allo stesso modo di un underflow aritmetico nel caso dei numeri in virgola mobile). Perciò viene sconsigliato l'uso di questi convertitori per qualsiasi software applicativo che richieda controlli stringenti sull'input.

Come alternativa si indicano le funzioni strtol e strtod, che effettuano dei controlli aggiuntivi sull'overflow/underflow.

L'applicativo per l'analisi statica del codice[13] in Clang (Clang-Tidy) sconsiglia dall'uso di scanf.[14]

Funzioni derivate

[modifica | modifica wikitesto]

fscanf

[modifica | modifica wikitesto]

La funzione fscanf legge i dati di input da un file, piuttosto che utilizzare l'input standard. Il suo funzionamento è sostanzialmente uguale a quello della sua primitiva e il suo prototipo (in C/C++) è:

int fscanf (FILE *file, const char *format, ...); // fino a C99
int fscanf (FILE *restrict file, const char *restrict format, ...); //dal C99

sscanf

[modifica | modifica wikitesto]

La funzione sscanf legge i dati di input da un buffer, piuttosto che utilizzare l'input standard, restituendo i valori che si possono leggere, e il suo prototipo (in C/C++) è:

int sscanf (char *buffer, const char *format, ...); // fino a C99
int sscanf (char *restrict buffer, const char *restrict format, ...); //dal C99

Altre funzioni derivate

[modifica | modifica wikitesto]

Dal C11 sono state introdotte ulteriori funzioni: scanf_s, fscanf_s, sscanf_s.

Esempi d'uso

[modifica | modifica wikitesto]
#include <stdio.h>
int main(void) 
{ 
    int i; 
    float fp; 
    char c, s[81]; 
    printf("Inserire un numero intero, un numero reale," 
            " un carattere e una stringa: \n"); 
    if (scanf("%d %f %c %s", &i, &fp, &c, s) != 4) 
        printf("Non tutti i campi sono stati compilati\n"); 
    else 
    { 
        printf("numero intero = %d\n", i); 
        printf("numero reale = %f\n", fp); 
        printf("carattere = %c\n", c); 
        printf("stringa = %s\n",s); 
    } 
}

Se l'input è 27 5.4 t si, allora l'uotput sarà simile a:

Inserire un numero intero, un numero reale, un carattere e una stringa:
numero intero = 27
numero reale = 5.400000
carattere = t
stringa = si

Uso dell'indicatore d'ampiezza

[modifica | modifica wikitesto]
#include <stdio.h>
int main()
{
    int i;
    float x, y;
    char name[50];
    printf ("Inserire due stringhe numeriche che contengano 8 caratteri"
            " ognuna e che abbiano entrambe un punto al centro:\n");
    scanf("%2d %f %6f %[0123456789]", &i, &x, &y, name);
    printf("numero intero = %d\n", i);
    printf("primo numero reale = %f\n", x);
    printf("secondo numero reale = %f\n", y);
    printf("il resto della stringa = %s\n",name);
}

Se l'input è 1234.4321 1234.4321, allora l'output sarà simile a

Inserire due stringhe numeriche che contengano almeno 8 caratteri ognuna e che abbiano entrambe un punto al centro:
numero intero = 12
primo numero reale = 34.432098
secondo numero reale = 1234.400024
resto della stringa = 321

Come si può notare, l'uso di uno definitore di virgola mobile permette la lettura del punto di separazione decimale, ma bisogna contare anche la lettura del punto perché è esso stesso un carattere (il 24 in coda al secondo numero reale è solo un errore di conversione binaria dei numeri decimali).

Uso in un ciclo

[modifica | modifica wikitesto]
#include <stdio.h>
int main()
{
   int numero;
   printf("Inserire un numero esadecimale o qualunque altra cosa diversa per uscire:\n");
   while (scanf("%x",&numero))
   {
      printf("Numero esadecimale = %x\n",numero);
      printf("Numero decimale    = %d\n",numero);
   }
}

Questo esempio converte un numero intero esadecimale in un numero intero decimale. Il loop while termina se il valore di input non è un numero intero esadecimale. Se l'input è 0x231 0xf5e 0x1 0x, allora l'output sarà simile a

Inserire un numero esadecimale o qualunque altra cosa diversa per uscire:
Numero esadecimale = 231
Numero decimale    = 561
Numero esadecimale = f5e
Numero decimale    = 3934
Numero esadecimale = 1
Numero decimale    = 1
Numero esadecimale = 0
Numero decimale    = 0

Uso del soppressore d'assegnazione (*)

[modifica | modifica wikitesto]
#include <stdio.h>
int main()
{
    char text[20];
    float number;
    printf("Inserire i dati:\n");
    scanf("%s %*d %f", text, &number);
    printf("output: %s %f\n", text, number);
    return 0;
}

Al momento di leggere la stringa, scanf non leggerà ciò che corrisponde a %*d, pertanto non c'è bisogno di assegnare l'indirizzo di un argomento per tale corrispondenza.[15] Pertanto, per un input del tipo prova 324.523, si otterrà un output del tipo:

Inserire i dati:
output: prova 0.523000

Uso dei puntatori

[modifica | modifica wikitesto]
#include <stdio.h>
int main()
{
    int val = 123;
    char buf[100];
    sprintf(buf, "%p", (void*)&val);
    printf("Original =%s\n", buf);
    void *ptr;
    sscanf(buf, "%p", &ptr);
    printf("Read back=%p\n", ptr);
    int *iPtr = ptr;
    if (iPtr == &val) {
        printf("Pointers match\n");
    }
}

La funzione sprintf alla riga 3 scrive il puntatore nel buffer di output; sscanf lo rilegge.

Alla riga 6, la stringa immessa in sscanf corrisponde esattamente alla stringa prodotta da sprintf, quindi lo standard garantisce che iPtr == &val verrà valutato come true e il comportamento sia pertanto definito. Passare una stringa che non corrisponde a nulla di ciò che è stato prodotto dallo stesso programma in esecuzione sarebbe un comportamento indefinito.[16]

Vulnerabilità

[modifica | modifica wikitesto]

scanf è vulnerabile ai format string attacks[17]. Bisogna pertanto prestare grande cura nell'assicurarsi che la stringa di formattazione contenga delle limitazioni riguardo la grandezza degli array (e quindi anche delle stringe) che vengono passati. In molti casi è arbitraria la grandezza della stringa inserita dall'utente e non può essere determinata prima che scanf venga eseguita. Ciò significa che i definitori di formato usati senza un limitatore di lunghezza sono intrinsecamente non sicuri e sfruttabili tramite buffer overflows.

Un'ulteriore vulnerabilità riguarda il permesso ad usare stringhe di formato dinamiche (dynamic formatting strings[18]), per esempio stringhe di formattazione contenute in file di configurazione o altri file accessibili dall'utente e di cui ne detenga i permessi di modifica. Un attacco noto che sfrutta questo genere di vulnerabilità è SQL injection.

Per questi ed altri motivi, ad oggi scanf viene messa in secondo piano durante l'apprendimento del C/C++, propendendo piuttosto verso fgets per l'acquisizione della riga, per poi passare la stessa a sscanf per scomporla (entrambe funzioni anch'esse contenute all'interno di stdio.h).[19]

Note

[modifica | modifica wikitesto]
  1. ↑ (EN) The Basics of C Programming, su HowStuffWorks, 1º aprile 2000. URL consultato il 14 novembre 2025.
  2. ↑ Michael Lesk's Home Page, su lesk.com. URL consultato il 14 novembre 2025.
  3. ↑ restrict type qualifier (since C99) - cppreference.com, su en.cppreference.com. URL consultato il 14 novembre 2025.
  4. ↑ C Input/Output: printf() and scanf(), su www.programiz.com. URL consultato il 14 novembre 2025.
  5. ↑ scanf, fscanf, sscanf, scanf_s, fscanf_s, sscanf_s - cppreference.com, su en.cppreference.com. URL consultato l'11 novembre 2025.
  6. ↑ (IT) Brian W. Kerninghan e Dennis M. Ritchie, 7, in Il linguaggio C [The C programming language], traduzione di Valerio Marra, Revisore: Vincenzo Marra, seconda edizione, Pearson, 2007, p. 161, ISBN 9788891908230.
  7. ↑ TylerMSFT, Campi per la specifica di formato: funzioni scanf e wscanf, su learn.microsoft.com. URL consultato il 14 novembre 2025.
  8. ↑ Fundamental types - cppreference.com, su en.cppreference.com. URL consultato il 14 novembre 2025.
  9. ↑ Arithmetic types - cppreference.com, su en.cppreference.com. URL consultato il 14 novembre 2025.
  10. ↑ fscanf, su pubs.opengroup.org. URL consultato il 14 novembre 2025.
  11. ↑ (EN) scanf in C, su GeeksforGeeks, 24 giugno 2022. URL consultato il 14 novembre 2025.
  12. ↑ scanf () - Leggi dati, su www.ibm.com. URL consultato il 14 novembre 2025.
  13. ↑ Che cos'è l'analisi statica del codice? - Software Check Point, su Check Point Software. URL consultato il 14 novembre 2025.
  14. ↑ clang-tidy - bugprone-unchecked-string-to-number-conversion — Extra Clang Tools 22.0.0git documentation, su clang.llvm.org. URL consultato il 14 novembre 2025.
  15. ↑ (EN) Assignment Suppression Character, su Stack Overflow. URL consultato il 14 novembre 2025.
  16. ↑ (EN) Using scanf %p UB, su Stack Overflow. URL consultato il 14 novembre 2025.
  17. ↑ (EN) Format string attack | OWASP Foundation, su owasp.org. URL consultato l'11 novembre 2025.
  18. ↑ (EN) Introducing dynamic format strings for DAX measures - SQLBI, su sqlbi.com. URL consultato l'11 novembre 2025.
  19. ↑ (EN) Vulnerability using printf/scanf and %s, su Stack Overflow. URL consultato il 14 novembre 2025.

Voci correlate

[modifica | modifica wikitesto]
  • Canali standard
  • printf
  Portale Informatica: accedi alle voci di Teknopedia che trattano di informatica
Estratto da "https://it.wikipedia.org/w/index.php?title=Scanf&oldid=148155816"

  • Indonesia
  • English
  • Français
  • 日本語
  • Deutsch
  • Italiano
  • Español
  • Русский
  • فارسی
  • Polski
  • 中文
  • Nederlands
  • Português
  • العربية
Pusat Layanan

UNIVERSITAS TEKNOKRAT INDONESIA | ASEAN's Best Private University
Jl. ZA. Pagar Alam No.9 -11, Labuhan Ratu, Kec. Kedaton, Kota Bandar Lampung, Lampung 35132
Phone: (0721) 702022