Confronto tra unsigned char ed EOF

quando viene compilato il seguente codice, esso entra in un ciclo infinito:

int main() { unsigned char ch; FILE *fp; fp = fopen("abc","r"); if(fp==NULL) { printf("Unable to Open"); exit(1); } while((ch = fgetc(fp))!=EOF) printf("%c",ch); fclose(fp); printf("\n",ch); return 0; } 

Il compilatore gcc avvisa anche sulla compilazione

 abc.c:13:warning: comparison is always true due to limited range of data type 

il codice funziona bene quando il unsigned char viene sostituito da char o int come previsto, ovvero termina.
Ma il codice funziona bene anche per i unsigned int . come ho ho letto in EOF è definito come -1 in stdio.h allora perché questo codice non riesce per il char unsigned ma funziona bene per int unsigned.

La regola d’oro per scrivere questa linea è

  while ((ch = fgetc(stdin)) != EOF) 

ch dovrebbe essere int tuo trucco carino di fare ch unsigned fallisce perché EOF è una quantità int signed.

Ok, andiamo ora nel profondo ……

Passo 1:

 ch=fgetc(fp) 

fgetc() restituisce -1 (un int firmato). Con le regole d’oro di C ch ottiene l’ultimo ottetto di bit che è tutto 1 ‘s. E quindi il valore 255 . Il modello di byte di ch dopo l’esecuzione di

 ch = fgetc(fp); 

sarebbe così

 11111111 

Passo 2:

 ch != EOF 

Ora EOF è un intero con unsigned char e ch è un unsigned char

Ancora una volta mi riferisco alla regola d’oro di C … il ragazzo più piccolo ch viene convertito in int di grandi dimensioni prima del confronto, quindi il suo modello di byte è ora

 00000000000000000000000011111111 = (255)10 

mentre EOF è

 11111111111111111111111111111111 = (-1)10 

Non c’è modo in cui possano essere uguali ……. Da qui l’affermazione di guidare il seguente ciclo while

 while ((ch = fgetc(stdin)) != EOF) 

non valuterò mai il falso …

E quindi il ciclo infinito.

Ci sono diverse conversioni implicite in corso. Non sono davvero rilevanti per l’avviso specifico, ma li ho inclusi in questa risposta per mostrare cosa fa realmente il compilatore con quell’espressione.

  • ch nel tuo esempio è di tipo char unsigned.
  • EOF è garantito di tipo int (C99 7.19.1).

Quindi l’espressione è equivalente a

 (unsigned char)ch != (int)EOF 

Le regole di promozione intera in C convertiranno implicitamente il char non firmato in unsigned int:

 (unsigned int)ch != (int)EOF 

Quindi le regole di bilanciamento (ovvero le solite conversioni aritmetiche ) in C converte implicitamente l’int in unsigned int, perché ogni operando deve avere lo stesso tipo:

 (unsigned int)ch != (unsigned int)EOF 

Sul tuo compilatore EOF è probabile -1:

 (unsigned int)ch != (unsigned int)-1 

che, supponendo CPU a 32 bit, è uguale a

 (unsigned int)ch != 0xFFFFFFFFu 

Un personaggio non può mai avere un valore così alto, quindi l’avvertimento.

Ho riscontrato anche questo problema. La mia soluzione è usare feof ().

 unsigned int xxFunc(){ FILE *fin; unsigned char c; fin = fopen("...", "rb"); if(feof(fin) != 0) return EOF; c = fgetc(fin); fclose(fin); ... } 

E puoi definire una variabile int da confrontare con EOF. Per esempio:

 int flag = xxFunc(); while(flag != EOF) {...} 

Questo funziona per me.

** AGGIORNAMENTO IMPORTANTE * **

Dopo aver usato il metodo che ho menzionato prima, ho trovato un problema serio. feof () non è un buon modo per interrompere il ciclo while. Ecco la ragione per questo. http://www.gidnetwork.com/b-58.html

Quindi trovo un modo migliore per farlo. Io uso una variabile int per farlo. Qui:

 int flag; unsigned char c; while((flag = fgetc(fin)) != EOF) { //so, you are using flag to receive, but transfer the value to c later. c = flag; ... } 

Dopo la mia prova, funziona.

devi usare un int

fgetc () restituisce un int specificatamente in modo che possa indicare la fine del file

funziona bene con il carattere firmato perché EOF (-1) è compreso nell’intervallo, ma si interromperà se si legge in un carattere con valore superiore a 127.

Usa un int, gettalo su un char dopo aver controllato per EOF

Quando confronti un int unsigned con un int firmato, converte l’int firmato a int unsigned e li confronta. Quindi quando stai leggendo il file con un int ‘ch’ senza segno, la lettura di un EOF ti dà 2 ^ 32 + 1 (su un dispositivo int a 4 byte) e quando lo confronta con EOF, converte EOF in unsigned che è anche 2 ^ 32 + 1 e quindi il programma si ferma!

Se si utilizza il char non firmato, quando si legge il file, la lettura di EOF restituisce 2 ^ 32 + 1 e questa verrà convertita nel carattere unsigned char, che tronca il valore nei primi 8 bit (su una macchina char a 1 byte) e fornisce un’uscita di 255. Quindi stai confrontando 255 e 2 ^ 32 + 1, causando un loop infinito.

Il problema qui sta troncando prima del confronto.

Se usi

 while((ch = fgetc(fp))!=(unsigned char)EOF) printf("%c",ch); 

il tuo programma funzionerà correttamente!

un avvertimento di sfilacciamento viene prodotto con questo tipo di implementazione

Confrontando il tipo ‘char’ con EOF

  // read the data in a buffer 611 ch = getc(csv_file); 612 while (ch != EOF) 

FIX:

 // read the data in a buffer while ((ch = getc(csv_file)) != EOF)