La risposta a questo è che fflush(stream)
è formalmente definito solo per i flussi di uscita, quindi fflush(stdout)
va bene, ma fflush(stdin)
no.
Lo scopo di fflush(stream)
è quello di far sì che il sistema operativo effettui il flush dei buffer nel file sottostante. Per un esempio di uso legittimo, gli studenti spesso hanno problemi come “il mio prompt non appare!” se fanno qualcosa come:
printf("Enter a number: ");
Tuttavia, scoprono che questo funziona benissimo:
printf("Enter a number:\n");
Ovviamente, non vogliono una newline dopo il loro prompt, quindi hanno un po’ un problema.
La ragione di questo è che l’output a stdout
è bufferizzato dal sistema operativo e il comportamento di default è (spesso) solo quello di scrivere effettivamente l’output sul terminale quando si incontra una newline. Aggiungere un fflush(stdout)
dopo il printf()
risolve il problema:
printf("Enter a number: ");fflush(stdout);
Ora, lavorando per analogia, la gente spesso pensa che fflush(stdin)
dovrebbe scartare qualsiasi input inutilizzato, ma se ci pensate un po’ non ha molto senso. Cosa significa “lavare” un buffer di input? Dove viene “scaricato”? Se si scarica un buffer di output, l’output viene inviato al file sottostante o al terminale, dove finirebbe comunque, ma dove finirebbe comunque l’input? Non c’è modo di saperlo! Quale dovrebbe essere il comportamento se i dati del flusso di input provengono da un file o da una pipe o da un socket? Non è affatto chiaro per i flussi di input quale dovrebbe essere il comportamento di fflush()
, ma è molto chiaro per i flussi di output in tutti i casi. Quindi, fflush()
è definito solo per i flussi di uscita.
La ragione per cui l’uso errato di fflush(stdin)
è diventato comune è che, molti anni fa, alcuni sistemi operativi hanno implementato uno schema in cui funzionava come molti si aspettavano, scartando l’input inutilizzato. Microsoft DOS è un buon esempio. Sorprendentemente, le versioni moderne di Linux implementano anche fflush()
per i flussi di input.
La cosa giusta da fare con l’input “extra” indesiderato del terminale è semplicemente leggerlo e non farci nulla. Questo è facile quasi quanto chiamare fflush(stdin)
, funziona ovunque, e non si basa su un comportamento formalmente indefinito.
Lo standard C dice:
Se stream punta a un flusso di uscita o a un flusso di aggiornamento in cui l’operazione più recente non era di input, la funzione fflush fa sì che qualsiasi dato non scritto per quel flusso sia consegnato all’ambiente host per essere scritto nel file; altrimenti, il comportamento non è definito.
POSIX dice (rinvia anche esplicitamente allo standard C):
Se stream punta a un flusso di uscita o a un flusso di aggiornamento in cui l’operazione più recente non era in ingresso, fflush() causa la scrittura su file di qualsiasi dato non scritto per quel flusso, …
Ma la manpage di Linux dice:
Per i flussi di uscita, fflush() forza una scrittura di tutti i dati bufferizzati nello spazio utente per il dato flusso di uscita o di aggiornamento tramite la funzione di scrittura sottostante del flusso. Per i flussi di ingresso, fflush() scarta qualsiasi dato bufferizzato che è stato recuperato dal file sottostante, ma che non è stato consumato dall’applicazione. Lo stato aperto del flusso non è influenzato.