Memorizzazione del carattere EOF (End of File) in un tipo di carattere


11

Ho letto nel libro The C Programming Language di Dennis Ritchie che intdeve essere usato per una variabile per contenere EOF - per renderlo sufficientemente grande da poter contenere il valore EOF - no char. Ma il seguente codice funziona bene:

#include<stdio.h> 

main()  { 
  char c; 
  c=getchar(); 
  while(c!=EOF)  { 
    putchar(c); 
    c=getchar(); 
  } 
} 

Quando non c'è più input, getcharrestituisce EOF. E nel programma sopra, la variabile c, con tipo di carattere, è in grado di trattenerla correttamente.

Perché funziona? Secondo la spiegazione nel libro sopra menzionato, il codice non dovrebbe funzionare.



5
È probabile che questo codice fallisca se leggi un carattere con il valore 0xff. La memorizzazione del risultato di getchar()in intrisolve il problema. La tua domanda è essenzialmente la stessa della domanda 12.1 nelle FAQ di comp.lang.c , che è una risorsa eccellente. (Inoltre, main()dovrebbe essere int main(void), e non farebbe male aggiungere un return 0;prima della chiusura }.)
Keith Thompson,

1
@delnan: l'articolo collegato non è del tutto corretto su come Unix tratta control-D. Non chiude il flusso di input; causa semplicemente qualsiasi fread () che sta bloccando sulla console per restituire immediatamente con tutti i dati non ancora letti. Molti programmi interpretano un ritorno a zero byte da fread () come indica EOF, ma il file rimarrà infatti aperto e in grado di fornire più input.
supercat

Risposte:


11

Il tuo codice sembra funzionare, perché le conversioni di tipo implicito accadono accidentalmente per fare la cosa giusta.

getchar()restituisce un intcon un valore che si adatta all'intervallo unsigned charo è EOF(che deve essere negativo, in genere è -1). Nota che di per EOFsé non è un personaggio, ma un segnale che non ci sono più personaggi disponibili.

Quando si memorizza il risultato da getchar()dentro c, ci sono due possibilità. Il tipo charpuò rappresentare il valore, nel qual caso è il valore di c. O il tipo char non può rappresentare il valore. In tal caso, non è definito cosa accadrà. I processori Intel tagliano semplicemente i bit alti che non rientrano nel nuovo tipo (riducendo efficacemente il valore modulo 256 per char), ma non dovresti fare affidamento su questo.

Il passo successivo è quello di confrontare ccon EOF. Così come EOFè int, cverrà convertito anche in un int, preservando il valore memorizzato in c. Se cpotrebbe memorizzare il valore della EOF, allora il confronto avrà successo, ma se cpotrebbe non memorizzare il valore, allora il confronto non riuscirà, perché non v'è stata una perdita irreparabile di informazioni durante la conversione EOFdi tipo char.

Sembra che il tuo compilatore abbia scelto di rendere il chartipo firmato e il valore di EOFabbastanza piccolo da adattarsi char. Se charnon fosse stato firmato (o se fosse stato utilizzato unsigned char), il test avrebbe avuto esito negativo, poiché unsigned charnon può contenere il valore di EOF.


Si noti inoltre che esiste un secondo problema con il codice. Poiché EOFnon è un personaggio in sé, ma lo costringi a un chartipo, è molto probabile che ci sia un personaggio là fuori che viene interpretato erroneamente come essere EOFe per metà dei possibili personaggi è indefinito se verrà elaborato correttamente.


È necessario forzare per digitare charvalori al di fuori dell'intervallo CHAR_MIN. La CHAR_MAXvolontà è necessaria per produrre un valore definito dall'implementazione, produrre un modello di bit che l'implementazione definisce come rappresentazione trap o generare un segnale definito dall'implementazione. Nella maggior parte dei casi, le implementazioni dovrebbero richiedere molto lavoro extra per fare qualcosa di diverso dalla riduzione del complemento a due. Se le persone del Comitato per gli Standard sottoscrivessero l'idea che i compilatori dovrebbero essere incoraggiati a implementare comportamenti coerenti con quelli della maggior parte degli altri compilatori in assenza di motivi per fare diversamente ...
supercat

... Considero tale coercizione come affidabile (per non dire che il codice non dovrebbe documentare le sue intenzioni, ma questo (signed char)xdovrebbe essere considerato più chiaro e altrettanto sicuro ((unsigned char)x ^ CHAR_MAX+1))-(CHAR_MAX+1).) Così com'è, non vedo alcuna probabilità di compilatori che implementano qualsiasi altro comportamento conforme allo standard di oggi; l'unico pericolo sarebbe che lo Standard potesse essere modificato per interrompere il comportamento nel presunto interesse dell '"ottimizzazione".
supercat

@supercat: lo standard è scritto in modo tale che nessun compilatore debba produrre codice con un comportamento che non è naturalmente supportato dal processore a cui si rivolge. La maggior parte del comportamento indefinito è presente perché (al momento della stesura dello standard) non tutti i processori si sono comportati in modo coerente. Con i compilatori che diventano più maturi, gli autori di compilatori hanno iniziato a trarre vantaggio dal comportamento indefinito per effettuare ottimizzazioni più aggressive.
Bart van Ingen Schenau,

Storicamente, l'intenzione dello standard era principalmente quella che descrivi, sebbene lo standard descriva alcuni comportamenti in modo sufficientemente dettagliato da richiedere ai compilatori di alcune piattaforme comuni di generare più codice di quanto sarebbe richiesto da una specifica più libera. La coercizione di tipo in int i=129; signed char c=i;è uno di questi comportamenti. Relativamente pochi processori hanno un'istruzione che renderebbe cuguale iquando si trova nell'intervallo da -127 a +127 e produrrebbe qualsiasi mappatura coerente di altri valori di ivalori nell'intervallo da -128 a +127 che differiva dalla riduzione del complemento a due, oppure. ..
supercat

... solleverebbe costantemente un segnale in questi casi. Poiché lo standard richiede che le implementazioni producano una mappatura coerente o innalzano costantemente un segnale, le uniche piattaforme in cui lo standard lascerebbe spazio a qualcosa di diverso dalla riduzione del complemento a due sarebbero cose come i DSP con hardware saturo-aritmetico. Per quanto riguarda la base storica di Undefined Behavior, direi che il problema non riguarda solo le piattaforme hardware. Anche su una piattaforma in cui l'overflow si comporterebbe in modo molto coerente, può essere utile avere una trappola per compilatore ...
supercat
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.