È sbagliato perché (in assenza di un errore di lettura) entra nel ciclo ancora una volta di quanto l'autore si aspetti. Se si verifica un errore di lettura, il ciclo non termina mai.
Considera il seguente codice:
/* WARNING: demonstration of bad coding technique!! */
#include <stdio.h>
#include <stdlib.h>
FILE *Fopen(const char *path, const char *mode);
int main(int argc, char **argv)
{
FILE *in;
unsigned count;
in = argc > 1 ? Fopen(argv[1], "r") : stdin;
count = 0;
/* WARNING: this is a bug */
while( !feof(in) ) { /* This is WRONG! */
fgetc(in);
count++;
}
printf("Number of characters read: %u\n", count);
return EXIT_SUCCESS;
}
FILE * Fopen(const char *path, const char *mode)
{
FILE *f = fopen(path, mode);
if( f == NULL ) {
perror(path);
exit(EXIT_FAILURE);
}
return f;
}
Questo programma stamperà costantemente uno maggiore del numero di caratteri nel flusso di input (presupponendo che non vi siano errori di lettura). Considera il caso in cui il flusso di input è vuoto:
$ ./a.out < /dev/null
Number of characters read: 1
In questo caso, feof()
viene chiamato prima che i dati siano stati letti, quindi restituisce false. Il ciclo viene inserito, fgetc()
viene chiamato (e restituisce EOF
) e il conteggio viene incrementato. Quindi feof()
viene chiamato e restituisce true, causando l'interruzione del loop.
Questo succede in tutti questi casi. feof()
non restituisce true fino a quando una lettura sullo stream non rileva la fine del file. Lo scopo di feof()
NON è verificare se la lettura successiva raggiungerà la fine del file. Lo scopo di feof()
è di distinguere tra un errore di lettura e aver raggiunto la fine del file. Se fread()
restituisce 0, è necessario utilizzare feof
/ ferror
per decidere se si è verificato un errore o se tutti i dati sono stati consumati. Allo stesso modo se fgetc
ritorna EOF
. feof()
è utile solo dopo che Fread ha restituito zero o fgetc
è tornato EOF
. Prima che ciò accada, feof()
restituirà sempre 0.
È sempre necessario controllare il valore restituito di una lettura (an fread()
, oppure an fscanf()
, o an fgetc()
) prima di chiamare feof()
.
Ancora peggio, considera il caso in cui si verifica un errore di lettura. In tal caso, fgetc()
restituisce EOF
, feof()
restituisce false e il ciclo non termina mai. In tutti i casi in cui while(!feof(p))
viene utilizzato, deve esserci almeno un controllo all'interno del ciclo per ferror()
, o almeno la condizione while deve essere sostituita con while(!feof(p) && !ferror(p))
o c'è una possibilità molto reale di un ciclo infinito, probabilmente vomitando ogni sorta di immondizia come dati non validi in fase di elaborazione.
Quindi, in sintesi, anche se non posso affermare con certezza che non c'è mai una situazione in cui potrebbe essere semanticamente corretto scrivere " while(!feof(f))
" (anche se ci deve essere un altro controllo all'interno del ciclo con una pausa per evitare un ciclo infinito su un errore di lettura ), è quasi sempre sempre sbagliato. E anche se un caso dovesse sorgere dove sarebbe corretto, è così idiomaticamente sbagliato che non sarebbe il modo giusto di scrivere il codice. Chiunque veda quel codice dovrebbe immediatamente esitare e dire "questo è un bug". E possibilmente schiaffeggiare l'autore (a meno che l'autore sia il tuo capo nel qual caso è consigliata la discrezione.)
feof()
per controllare un loop