Introduzione alla funzione scanf in linguaggio C

La funzione scanf è una funzione della libreria standard del linguaggio C che permette di leggere dati da input standard (solitamente la tastiera) e di memorizzarli in variabili.

Si tratta di una delle funzioni più comunemente adopearate per l'input in C, ed è spesso utilizzata in combinazione con la funzione printf, che invece serve per l'output. Per questo motivo, in questa lezione ci concentreremo sull'uso di scanf per leggere dati da input.

Concetti Chiave
  • La funzione scanf legge dati dall'input standard e li memorizza in variabili.
  • La funzione scanf utilizza una stringa di formato per specificare il tipo di dati da leggere.
  • Gli specificatori di formato di scanf sono simili a quelli di printf.
  • È necessario passare gli indirizzi delle variabili a scanf usando l'operatore di indirizzo &.
  • scanf ignora automaticamente gli spazi bianchi nell'input.
  • È importante che il numero e i tipi di specificatori di formato corrispondano agli argomenti passati a scanf.
  • Dimenticare l'operatore di indirizzo & con scanf è un errore comune che può portare a comportamenti imprevedibili del programma.
  • scanf non consuma tutto l'input, lasciando caratteri non letti disponibili per successive chiamate.
  • scanf effettua il riconoscimento di pattern per abbinare l'input agli specificatori di formato.
  • Caratteri normali nella stringa di formato influenzano il modo in cui scanf legge l'input.

Sintassi e uso della funzione scanf

Così come la funzione printf stampa dati sull'output in modo formattato, la funzione scanf legge dati dall'input secondo un formato specificato dall'utente. Per farlo, anche la funzione scanf si aspetta in ingresso una stringa di formato che specifica il tipo di dati da leggere e come interpretarli.

Le stringhe di formato utilizzate da scanf sono simili a quelle di printf. Possono contenere sia caratteri normali (che vengono ignorati durante la lettura) sia specificatori di formato (che indicano il tipo di dato da leggere). Gli specifictori di formato sono identici a quelli adoperati da printf.

In molti casi, le chiamate a scanf contengono esclusivamente specificatori di formato. Consideriamo il seguente esempio:

int a, b;
double x, y;

scanf("%d%d%lf%lf", &a, &b, &x, &y);

Supponiamo che l'utente inserisca la seguente riga di input seguita dalla pressione del tasto Invio:

42 7 3.14 2.71

La funzione scanf leggerà la riga e ne convertirà i caratteri nei numeri che essi rappresentano. Dopodiché, i valori convertiti verranno memorizzati nelle variabili a, b, x e y. In questo caso, a conterrà il valore 42, b conterrà il valore 7, x conterrà il valore 3.14 e y conterrà il valore 2.71.

Questo schema di adoperare esclusivamente specificatori di formato è molto comune quando si usa la funzione scanf. Viceversa, con la funzione printf è più comune adoperare una combinazione di specificatori di formato e caratteri normali per formattare l'output in modo leggibile.

Definizione

Funzione scanf

La funzione scanf legge dati dall'input standard (solitamente la tastiera) e li memorizza in variabili secondo un formato specificato dall'utente.

La sintassi della funzione è la seguente:

scanf(stringa_di_formato, &argomento1, &argomento2, ...);

Dove stringa_di_formato è una stringa che specifica il formato dei dati da leggere, e argomento1, argomento2, ... sono le variabili in cui memorizzare i dati letti.

Sebbene a prima vista l'uso di scanf possa sembrare semplice, esso nasconde diverse insidie e complessità che è importante conoscere per evitare errori comuni durante la lettura dell'input.

Per prima cosa, quando un programmatore adopera la funzione scanf, deve sempre assicurarsi che il numero di specificatori di formato corrisponda al numero di argomenti passati alla funzione. Inoltre, è fondamentale che i tipi di dato specificati nella stringa di formato corrispondano ai tipi delle variabili in cui i dati verranno memorizzati. Infatti, lo standard del linguaggio C non impone né controlli sul numero di argomenti né sui tipi di dato. Pertanto, se il programmatore commette un errore in questo senso, il comportamento del programma sarà indefinito.

La seconda cosa importante da notare è che, a differenza di printf, la funzione scanf richiede che gli vengano passati gli indirizzi delle variabili in cui memorizzare i dati letti. Questo è necessario perché scanf deve poter modificare il contenuto delle variabili, e per farlo ha bisogno di conoscere il loro indirizzo in memoria. Per ottenere l'indirizzo di una variabile, si usa l'operatore & (operatore di indirizzo). Questo operatore è (quasi) sempre necessario quando si adopera scanf. Torneremo su questo punto più avanti quando studieremo i puntatori.

Nota

Mai dimenticare l'operatore di indirizzo & con scanf

Dimenticare di usare l'operatore di indirizzo & davanti gli argomenti di scanf è un errore comune che può portare a comportamenti imprevedibili se non disastrosi del programma. Uno dei risultati più comuni di questo errore è il crash del programma durante l'esecuzione.

Un altro possibile risultato è che scanf scriva i dati letti in posizioni di memoria errate, mentre la variabile originale rimane invariata.

Omettere l'operatore di indirizzo & è un errore di programmazione talmente comune in C che molti compilatori moderni emettono un avviso quando rilevano questo errore.

Ad esempio, gcc emette il seguente avviso:

warning: format ‘%d’ expects argument of type ‘int *’, but argument 2 has type ‘int’ [-Wformat=]

In questo avviso, il compilatore segnala che lo specificatore di formato %d si aspetta un argomento di tipo int * (puntatore a intero), ma l'argomento effettivamente passato è di tipo int (intero). Torneremo sui puntatori più avanti nel corso.

La funzione scangf è una funzione molto potente e flessibile, ma il suo uso corretto richiede attenzione ai dettagli. Molti programmatori professionali del linguaggio C evitano di usare scanf e preferiscono altre tecniche di lettura dell'input, come la lettura di intere righe che poi vengono analizzate con funzioni specifiche.

In questo corso, almeno nelle lezioni introduttive, continueremo a usare scanf per semplicità, perché permette di leggere facilmente input numerici dalla console. Tuttavia, molti dei programmi che realizzeremo nelle prossime lezioni non si comportano correttamente se l'input non è esattamente quello atteso.

Vedremo, quando studieremo l'input e l'output in modo più approfondito, come sia possibile controllare se una chiamata a scanf è andata a buon fine e come gestire gli errori di input in modo più robusto. Tuttavia, per ora, ometteremo tali controlli che appesantirebbero inutilmente i programmi di esempio.

Funzionamento di scanf

Abbiamo visto come usare la funzione scanf per leggere dati dall'input standard. Tuttavia, tale funzione è molto più potente di quanto sembri.

Nella sostanza, si può considerare scanf come una funzione di pattern-matching (ossia di riconoscimento di modelli). Essa cerca di abbinare gruppi di caratteri letti dall'input a specificatori di formato presenti nella stringa di formato.

Come la funzione printf, anche scanf è controllata dalla stringa di formato. Quando viene invocata, scanf inizia a processare i caratteri della stringa di formato da sinistra a destra. Ogniqualvolta incontra uno specificatore di formato, scanf cerca di localizzare nell'input un gruppo di caratteri che corrisponda a quel formato. Nel far questo, essa ignora automaticamente gli spazi bianchi (spazi, tabulazioni e caratteri di nuova linea) presenti nell'input se necessario.

Quando trova un elemento che corrisponde al formato specificato, scanf converte quel gruppo di caratteri nel tipo di dato appropriato e lo memorizza nella variabile il cui indirizzo è stato passato come argomento. Viceversa, essa si ferma solo nel caso in cui incontra un carattere che non può in alcun modo essere abbinato al formato specificato. Se va tutto bene, scanf continua a processare la stringa di formato fino a quando non ha elaborato tutti gli specificatori di formato presenti.

Consideriamo un esempio. Supponiamo di avere il seguente codice:

int x, y;
scanf("%d%d", &x, &y);

Supponiamo, per prima cosa, che l'utente inserisca la seguente riga di input:

  12   34

Quindi, l'input è composto da 2 spazi iniziali, seguiti dal numero 12, seguito da 3 spazi, seguito dal numero 34.

Quando scanf elabora questo input, essa inizia a processare la stringa di formato. Il primo specificatore di formato è %d, che indica che deve leggere un intero decimale. scanf ignora gli spazi iniziali e trova il numero 12, lo converte in un intero e lo memorizza nella variabile x.

Successivamente, scanf continua a processare la stringa di formato e incontra il secondo specificatore di formato %d. Essa ignora gli spazi successivi al numero 12 e trova il numero 34, lo converte in un intero e lo memorizza nella variabile y.

A questo punto, scanf ha elaborato tutti gli specificatori di formato presenti nella stringa e termina l'esecuzione. Alla fine, la variabile x conterrà il valore 12 e la variabile y conterrà il valore 34.

Viceversa, supponiamo che l'utente inserisca la seguente riga di input:

  56  abc78

In questo caso, scanf inizia a processare la stringa di formato e incontra il primo specificatore di formato %d. Essa ignora gli spazi iniziali e trova il numero 56, lo converte in un intero e lo memorizza nella variabile x.

Successivamente, scanf continua a processare la stringa di formato e incontra il secondo specificatore di formato %d. Essa ignora lo spazio successivo al numero 56 e trova la sequenza di caratteri abc78. Tuttavia, questa sequenza non può essere convertita in un intero decimale, poiché inizia con lettere. Pertanto, scanf si ferma in questo punto e non elabora ulteriormente la stringa di formato. Alla fine, la variabile x conterrà il valore 56, mentre la variabile y rimarrà invariata.

Il punto importante è che scanf ignora qualsiasi spazio bianco: con questo intendiamo spazi, tabulazioni e caratteri di nuova linea. Pertanto, se avessimo passato come input:

78
   90

scanf avrebbe comunque letto correttamente i numeri 78 e 90, ignorando gli spazi e il carattere di nuova linea tra di essi.

In pratica, dal punto di vista della funzione, l'input di sopra corrisponde a:

78\n   90

Dove \n rappresenta il carattere di nuova linea.

Chiamate successive a scanf

C'è un dettaglio importante da considerare quando si fanno chiamate successive a scanf. La funzione scanf, per poter interpretare correttamente l'input, sbircia sempre il carattere successivo nell'input per determinare se deve ignorarlo o meno.

Quindi, se ad esempio abbiamo il seguente codice:

int a, b;

scanf("%d", &a);
scanf("%d", &b);

E l'utente inserisce il seguente input:

12
34

Ossia, inserisce il numero 12, preme Invio, quindi inserisce il numero 34 e preme nuovamente Invio, quello che accade è che la prima chiamata a scanf legge il numero 12 e memorizza il valore nella variabile a. Dopodiché, la funzione vede il carattere di nuova linea (\n) generato dalla pressione del tasto Invio ma lo lascia nell'input, poiché non è necessario ignorarlo in questo caso.

La chiamata successiva a scanf inizia a leggere dall'input proprio dal carattere di nuova linea. Tuttavia, poiché scanf ignora automaticamente gli spazi bianchi, essa ignora il carattere di nuova linea e legge il numero 34, memorizzandolo nella variabile b. Essa, però, ignorerà e lascerà nell'input il carattere di nuova linea generato dalla seconda pressione del tasto Invio.

Questo è un dettaglio fondamentale da ricordare, soprattutto quando, come vedremo, si combinano chiamate a scanf con altre funzioni di input che non ignorano automaticamente gli spazi bianchi.

Definizione

La funzione scanf non consuma tutto l'input

Spesso, i programmatori alle prime armi si aspettano che la funzione scanf consumi tutto l'input fino alla fine della riga. Tuttavia, questo non è vero. La funzione scanf legge solo i caratteri necessari per soddisfare gli specificatori di formato presenti nella stringa di formato. Tutto ciò che rimane nell'input dopo la lettura non viene consumato e rimane disponibile per le chiamate successive a scanf o ad altre funzioni di input compreso il carattere di nuova linea generato dalla pressione del tasto Invio.

Regole di scanf per i numeri

Adesso, ci chiediamo quali sono le regole specifiche che scanf adotta per leggere i numeri. In particolare, ci chiediamo quali caratteri scanf considera validi quando legge un numero.

Quando chiediamo alla funzione di leggere un numero intero (ad esempio, con lo specificatore di formato %d), essa cerca, per prima cosa, un eventuale cifra oppure un segno + o -. A questo punto, essa continua a leggere i caratteri successivi finché incontra cifre valide. Non appena incontra un carattere che non è una cifra, scanf si ferma e converte il gruppo di caratteri letti in un numero intero.

Viceversa, quando chiediamo alla funzione di leggere un numero in virgola mobile, la funzione cerca:

  • Un segno + o - (opzionale), seguito da
  • Una sequenza di cifre (opzionale) possibilmente contenenti un unico punto decimale . (opzionale), seguito da
  • Un'esponente scientifico (opzionale) che inizia con la lettera e o E, seguita da un segno + o - (opzionale) e da una sequenza di cifre.

In tal senso, gli specificatori di formato per i numeri in virgola mobile, %lf, %lg e %le, sono equivalenti e non fanno distinzione tra i vari formati di input. Tutti e tre accettano numeri in virgola mobile in notazione decimale o scientifica.

Quando la funzione scanf incontra un carattere che non può essere parte di un numero, essa lo rimette a posto nell'input e si ferma. Questo significa che il carattere non viene consumato e rimane disponibile per i successivi specificatori di formato o per altre chiamate a scanf o ad altre funzioni di input.

Chiariamo questo concetto con un esempio. Supponiamo di avere il seguente codice:

int a, b;
double x, y;

scanf("%d%d%lf%lf", &a, &b, &x, &y);

A questo punto, supponiamo che l'utente inserisca il seguente input:

1-30.4-5.0e2a

Ecco come scanf elaborerebbe questo input:

  1. Il primo specificatore di formato è %d, quindi scanf si aspetta un intero.

    Il primo carattere non di spaziatura è 1, che è una cifra valida. Quindi, scanf legge il carattere 1 e continua a leggere i caratteri successivi. Ma il carattere successivo è -, che non è una cifra valida.

    Quindi, scanf si ferma qui e rimette il carattere - nell'input. Dopodiché, converte il carattere 1 in un intero, memorizzandolo nella variabile a.

  2. Il secondo specificatore di formato è %d, quindi scanf si aspetta un altro intero.

    Il primo carattere non di spaziatura è -, che è un segno valido per un numero intero. Quindi, scanf legge il carattere - e continua a leggere i caratteri successivi. Il carattere successivo è 3, che è una cifra valida, quindi scanf legge anche questo carattere. Il carattere successivo è 0, che è anch'esso una cifra valida, quindi scanf legge anche questo carattere.

    Il carattere successivo è ., che non è una cifra valida. Quindi, scanf si ferma qui, rimette . nell'input, e converte la sequenza di caratteri -30 in un intero, memorizzandolo nella variabile b.

  3. Il terzo specificatore di formato è %lf, quindi scanf si aspetta un numero in virgola mobile.

    Il primo carattere non di spaziatura è ., che è un punto decimale valido per un numero in virgola mobile. Quindi, scanf legge il carattere . e continua a leggere i caratteri successivi. Il carattere successivo è 4, che è una cifra valida, quindi scanf legge anche questo carattere. Il carattere successivo è -, che non è né una cifra né un punto decimale valido.

    Quindi, scanf si ferma qui, rimette il - nell'input e converte la sequenza di caratteri .4 in un numero in virgola mobile, memorizzandolo nella variabile x che ora contiene il valore 0.4.

  4. Il quarto specificatore di formato è %lf, quindi scanf si aspetta un altro numero in virgola mobile.

    Il primo carattere non di spaziatura è -, che è un segno valido per un numero in virgola mobile. Quindi, scanf legge il carattere - e continua a leggere i caratteri successivi. Il carattere successivo è 5, che è una cifra valida, quindi scanf legge anche questo carattere. Il carattere successivo è . , che è un punto decimale valido, quindi scanf legge anche questo carattere. I successivi caratteri sono 0, e, 2, che sono tutti validi per un numero in virgola mobile in notazione scientifica, quindi scanf legge anche questi caratteri.

    Infine, il carattere successivo è a, che non è né una cifra, né un punto decimale, né parte di un esponente scientifico valido. Quindi, scanf si ferma qui, rimette a nell'input e converte la sequenza di caratteri -5.0e2 in un numero in virgola mobile, memorizzandolo nella variabile y che ora contiene il valore -500.0.

Alla fine di questa elaborazione, le variabili conterranno i seguenti valori:

  • a conterrà il valore 1
  • b conterrà il valore -30
  • x conterrà il valore 0.4
  • y conterrà il valore -500.0

Nell'input, invece, rimarrà il carattere a ed il carattere di nuova linea generato dalla pressione del tasto Invio. Questi caratteri potranno essere letti da successive chiamate a scanf o ad altre funzioni di input.

Caratteri normali nella stringa di formato

Il fatto che la funzione scanf effettui una sorta di riconoscimento di pattern (pattern-matching) implica che la stringa di formato può contenere anche caratteri normali, oltre agli specificatori di formato.

Le azioni che la funzione scanf compie quando incontra un carattere normale nella stringa di formato dipendono dal tipo di carattere: se si tratta o meno di uno spazio bianco. Vediamolo in dettaglio.

  • Spazi bianchi:

    Quando scanf incontra uno o più spazi bianchi (spazio, tabulazione o carattere di nuova linea) nella stringa di formato, essa legge e ignora tutti i caratteri di spaziatura presenti nell'input fino a quando non incontra un carattere non di spaziatura che viene rimesso nell'input per essere elaborato dal successivo specificatore di formato.

    La cosa importante da notare è che il numero di spazi bianchi nella stringa di formato non importa: uno, due o più spazi bianchi hanno lo stesso effetto.

    La conseguenza è che inserire un carattere di spazio bianco nella stringa di formato non impone il fatto che l'input ne debba necessariamente contenere uno. Infatti, uno spazio bianco corrisponde a qualsiasi quantità di spazi bianchi nell'input, compresa la possibilità che tale numero sia zero.

    Pertanto, il seguente codice:

    int a, b;
    scanf("%d %d", &a, &b);
    

    è equivalente a:

    int a, b;
    scanf("%d%d", &a, &b);
    

    Ossia, entrambi i codici leggono due numeri interi separati da qualsiasi quantità di spazi bianchi.

  • Caratteri normali non di spaziatura:

    Quando scanf incontra un carattere normale non di spaziatura nella stringa di formato, essa compara tale carattere con il carattere successivo nell'input.

    Se i due caratteri corrispondono, scanf li consuma entrambi (in altre parole, li legge e li scarta) e continua a processare la stringa di formato.

    Viceversa, se i due caratteri non corrispondono, scanf si ferma e rimette il carattere non corrispondente nell'input e si ferma non processando ulteriormente la stringa di formato.

Per chiarire questo concetto, consideriamo un esempio in cui vogliamo leggere una data in formato GG/MM/AAAA. Per farlo, possiamo usare il seguente codice:

int giorno, mese, anno;
scanf("%d/%d/%d", &giorno, &mese, &anno);

Supponiamo che l'utente inserisca il seguente input:

     15/08/2023

La funzione scanf elabora questo input come segue:

  1. Il primo specificatore di formato è %d, quindi scanf scarta i primi spazi bianchi e legge il numero 15, memorizzandolo nella variabile giorno.
  2. Successivamente, scanf incontra il carattere / nella stringa di formato. Essa confronta questo carattere con il carattere successivo nell'input, che è anch'esso /. Poiché i due caratteri corrispondono, scanf li consuma entrambi e continua a processare la stringa di formato.
  3. Il secondo specificatore di formato è %d, quindi scanf legge il numero 08, memorizzandolo nella variabile mese.
  4. Successivamente, scanf incontra nuovamente il carattere / nella stringa di formato. Essa confronta questo carattere con il carattere successivo nell'input, che è anch'esso /. Poiché i due caratteri corrispondono, scanf li consuma entrambi e continua a processare la stringa di formato.
  5. Il terzo specificatore di formato è %d, quindi scanf legge il numero 2023, memorizzandolo nella variabile anno.

Alla fine di questa elaborazione, le variabili conterranno i seguenti valori:

  • giorno conterrà il valore 15
  • mese conterrà il valore 8
  • anno conterrà il valore 2023

Lo stesso codice di sopra, però. non funzionerebbe correttamente se l'utente inserisse il seguente input:

15 / 08 / 2023

Questo perché, dopo aver letto il numero 15, scanf incontra il carattere / nella stringa di formato e lo confronta con il carattere successivo nell'input, che in questo caso è uno spazio. Poiché i due caratteri non corrispondono, scanf si ferma e rimette lo spazio nell'input, senza processare ulteriormente la stringa di formato.

Per poter funzionare correttamente con questo tipo di input, dovremmo modificare la stringa di formato per includere spazi bianchi prima e dopo i caratteri /, come segue:

int giorno, mese, anno;
scanf("%d / %d / %d", &giorno, &mese, &anno);

In questo modo, scanf ignorerà automaticamente gli spazi bianchi presenti nell'input prima e dopo i caratteri /, permettendo di leggere correttamente la data anche se l'utente inserisce spazi aggiuntivi.

Errori comuni con scanf

Sebbene le funzioni printf e scanf siano simili nella sintassi e adoperino specificatori di formato analoghi, ci sono alcune differenze importanti che possono portare a errori comuni quando si usa scanf. Soprattutto quando si confonde il loro uso.

Un primo errore comune è quello di usare l'operatore di indirizzo & anche con la funzione printf. Come abbiamo visto, printf non richiede gli indirizzi delle variabili, ma i loro valori. Pertanto, usare l'operatore & con printf è un errore che porta a comportamenti imprevedibili del programma:

/* ERRORE */
int a = 42;
printf("Il valore di a è: %d\n", &a); // ERRORE

Quello che succede in questo caso è che printf interpreta l'indirizzo di a come un intero, stampando un valore che non ha alcun senso.

Un secondo aspetto riguarda il fatto che scanf scarta in automatico tutti gli spazi bianchi nell'input. Quindi, le stringhe di formato di scanf devono, quasi sempre, contenere solo specificatori di formato. Molti errori nascono dal fatto che i programmatori alle prime armi si aspettano che le stringhe di formato di scanf debbano essere formattate allo stesso modo di quelle di printf. La conseguenza è che, così facendo, si ottengono comportamenti inattesi.

Ad esempio, consideriamo il seguente codice:

int a, b;
scanf("%d, %d", &a, &b);

In questo esempio, scanf cerca per prima cosa un intero (%d) che memorizza nella variabile a. Successivamente, essa cerca il carattere , nella stringa di formato e se l'utente inserisce, invece, uno spazio scanf si fermerà, poiché il carattere successivo nell'input non corrisponde a quello atteso. Così facendo, la variabile a conterrà il valore letto, mentre la variabile b rimarrà invariata.

Nota

Mai inserire un carattere di nuova riga al termine della stringa di formato di scanf

Sebbene le stringhe di formato passate alla funzione printf terminino spesso con un carattere di nuova linea (\n), fare la stessa cosa con la funzione scanf non è una buona idea.

Per la funzione scanf, infatti, un carattere di nuova linea nella stringa di formato viene interpretato come uno spazio bianco. L'effetto è che scanf cercherà di ignorare tutti i caratteri di spaziatura nell'input fino a quando non incontra un carattere non di spaziatura.

Questo può portare a comportamenti inattesi. Ad esempio, consideriamo il seguente codice:

int a;
scanf("%d\n", &a);

In questo esempio, scanf legge un intero e lo memorizza nella variabile a. Tuttavia, a causa del carattere di nuova linea (\n) alla fine della stringa di formato, scanf cercherà di ignorare tutti i caratteri di spaziatura successivi nell'input. Di conseguenza, se l'utente preme Invio dopo aver inserito il numero, scanf rimarrà in attesa di ulteriori input, poiché considera il carattere di nuova linea come uno spazio bianco da ignorare.

In altre parole, il programma sembrerà bloccarsi, in attesa che l'utente inserisca ulteriori caratteri non di spaziatura.

Per evitare questo problema, è meglio non includere mai un carattere di nuova linea (\n) alla fine della stringa di formato di scanf. Invece, si dovrebbe semplicemente usare:

int a;
scanf("%d", &a);

Esempio

Concludiamo questa lezione con un esempio completo che mostra l'uso della funzione scanf per leggere diversi tipi di dati dall'input standard.

Supponiamo di voler realizzare un programma che legge dall'input due frazioni e ne calcoli il prodotto. Una frazione è composta da un numeratore e un denominatore, entrambi interi. Per semplicità, supponiamo che l'utente inserisca le frazioni nel formato N/D, dove N è il numeratore e D è il denominatore.

Ecco un possibile codice per realizzare questo programma:

#include <stdio.h>

int main() {
    // Numeratore e denominatore della prima frazione
    int num1, den1;
    // Numeratore e denominatore della seconda frazione
    int num2, den2;
    // Numeratore e denominatore del prodotto
    int prod_num, prod_den;

    // Leggi la prima frazione dall'input
    printf("Inserisci la prima frazione (N/D): ");
    scanf("%d/%d", &num1, &den1);

    // Leggi la seconda frazione dall'input
    printf("Inserisci la seconda frazione (N/D): ");
    scanf("%d/%d", &num2, &den2);

    // Calcola il prodotto delle due frazioni
    prod_num = num1 * num2;
    prod_den = den1 * den2;

    // Stampa il risultato
    printf("Il prodotto delle frazioni è: %d/%d\n", prod_num, prod_den);

    return 0;
}

Provando a compilare ed eseguire questo programma, l'utente può inserire due frazioni nel formato N/D, e il programma calcolerà e stamperà il prodotto delle due frazioni. Ad esempio:

Inserisci la prima frazione (N/D): 3/4
Inserisci la seconda frazione (N/D): 2/5
Il prodotto delle frazioni è: 6/20