2011-12-21 7 views
6

quando il seguente codice viene compilato va in un ciclo infinito:Confrontando unsigned char e EOF

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 dà anche avvertimento sulla compilation

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

il codice viene eseguito bene quando unsigned char è sostituito da char o int come previsto, cioè termina.
Ma anche il codice funziona correttamente per unsigned int. come ho ho letto in EOF si definisce come -1 in stdio.h quindi perché questo codice non riesce per il char unsigned ma funziona bene per int unsigned.

+2

essenzialmente la stessa [fgetc non identifica EOF] (http://stackoverflow.com/questions/3977223/fgetc-does-not-identify-eof). Penso che abbiamo questo tipo di domanda almeno una volta alla settimana. –

+2

Vedere anche http://c-faq.com/stdio/getcharc.html –

+0

Possibile duplicato di [Perché la variabile utilizzata per contenere il valore restituito da getchar deve essere dichiarata come int?] (http://stackoverflow.com/questions/18013167/why-must-the-variable-used-to-hold-getchars-return-value-be-declared-as-int) –

risposta

7

La regola d'oro per la scrittura di questa linea è

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

ch dovrebbe essere int .Your simpatico trucco di far ch unsigned fallisce perché EOF è una quantità int firmato.

Ok, andiamo ora andare in profondità ......

Fase 1:

ch=fgetc(fp) 

fgetc() rendimenti -1 (una firmata int). Con le regole d'oro di C ch ottiene l'ultimo ottetto di bit che è tutto 1. E quindi il valore 255. Il modello byte ch dopo l'esecuzione di

ch = fgetc(fp); 

Sarebbe pertanto

11111111 

Fase 2:

ch != EOF 

Ora EOF è un numero intero firmata e ch è un unsigned char ...

Ancora una volta mi riferisco alla regola d'oro di C ...il ragazzo più piccolo ch viene convertito in formato grande int prima di confronto per cui il suo modello di byte è ora

00000000000000000000000011111111 = (255)10 

mentre EOF è

11111111111111111111111111111111 = (-1)10 

Non c'è modo che possano essere uguali ... .... Da qui l'affermazione di guidare il seguente ciclo while

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

non valuterà mai su falso ...

E quindi il ciclo infinito.

+1

La regola d'oro è ** corrisponde sempre alle parentesi **. Manca un ')' in entrambi gli esempi, deve essere 'while ((ch = fgetc (stdin))! = EOF)'. – Jens

+0

Ho appena reso la tua risposta chiara un po 'più carina. Ancora, potresti per favore approfondire ciò che vuoi per esprimere questo modello: '... = (255) 10' e' ... = (-1) 10'? – alk

+0

@alk Questa risposta è stata scritta dallo "studente me", quindi dall'incoerenza delle notazioni. (255) 10 indica 255 in base 10 – bashrc

1

è necessario utilizzare un int

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

funziona bene con char firmato perché EOF (-1) è nel range , ma si spezzerebbe se si legge in un char con valore superiore a 127.

Utilizzare un int, cast a un char dopo aver controllato per EOF

+0

so che dovrebbe essere usato in codice corretto ma voglio sapere perché il char non firmato non funziona ma int unsigned funziona ... –

+0

come -1 è nell'intervallo di unsigned int –

+2

Leggi le regole di promozione intera. –

0

Quando confronti un int unsigned con un int firmato, converte l'int con segno 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 char unsigned, che tronca il valore nei primi 8 bit (su una macchina di char di 1 byte) e ti dà un'uscita di 255. Quindi stai confrontando 255 e 2^32 + 1, causando un loop infinito.

Il problema qui viene troncato prima del confronto.

Se si utilizza

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

si programma verrà eseguito bene!

6

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 nell'esempio è di tipo carattere senza segno.
  • EOF è garantito di tipo int (C99 7.19.1).

Quindi l'espressione è equivalente a

(unsigned char)ch != (int)EOF 

Le regole di promozione interi in C sarà convertire implicitamente il unsigned char a unsigned int:

(unsigned int)ch != (int)EOF 

allora le regole di bilanciamento (aka le solite conversioni aritmetiche) in C implicitamente converte l'int in unsigned int, perché ogni operando deve avere il sa Mi Tipo:

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

Sul EOF compilatore è probabile -1:

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

che, assumendo CPU a 32 bit, è lo stesso che

(unsigned int)ch != 0xFFFFFFFFu 

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

2

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); 
... 
} 

Ed è possibile definire una variabile int da confrontare con EOF. Ad esempio:

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

Questo funziona per me.

**AGGIORNAMENTO IMPORTANTE* **

Dopo aver utilizzato il metodo che ho detto 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 il test, funziona.

0

un avvertimento lanugine è prodotto con questo tipo di implementazione

tipo 'char' Paragonando 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)