Memorizzazione del carattere EOF (End of File) in un carattere di tipo
Su Febbraio 18, 2021 da admin Ho letto nel linguaggio di programmazione C di Dennis Ritchie prenota che int
deve essere utilizzato affinché una variabile contenga EOF – per renderla sufficientemente grande in modo che possa contenere il valore EOF – non char
. Ma il seguente codice funziona bene:
#include<stdio.h> main() { char c; c=getchar(); while(c!=EOF) { putchar(c); c=getchar(); } }
Quando cè non è più un input, getchar
restituisce EOF. E nel programma precedente, la variabile c
, di tipo char, è in grado di mantenerlo con successo.
Perché funziona? Secondo la spiegazione nel libro sopra menzionato, il codice non dovrebbe funzionare.
Commenti
Risposta
Il tuo codice sembra funzionare, perché le conversioni di tipo implicite si verificano accidentalmente per fare la cosa giusta.
getchar()
restituisce un int
con un valore che rientra nellintervallo di unsigned char
o è EOF
( che deve essere negativo, di solito è -1). Tieni presente che EOF
in sé non è un carattere, ma un segnale che non ci sono più caratteri disponibili.
Quando si memorizza il risultato da getchar()
in c
, ci sono due possibilità. Il tipo char
può rappresentare il valore, nel qual caso si tratta del valore di c
. Oppure il tipo char
non può rappresentare il valore. In tal caso, non è definito cosa accadrà. I processori Intel si limitano a tagliare 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 passaggio successivo consiste nel confrontare c
con EOF
. Poiché EOF
è anche un int
, c
verrà convertito in un int
, preservando il valore memorizzato in c
. Se c
può memorizzare il valore di EOF
, il confronto avrà esito positivo , ma se c
è stato in grado di non memorizzare il valore, il confronto non riuscirà, perché si è verificata una perdita irreversibile di informazioni durante la conversione di EOF
per digitare char
.
Sembra che il tuo compilatore abbia scelto di impostare il tipo char
firmato e il valore di EOF
piccolo abbastanza per entrare in char
. Se char
non fosse firmato (o se avessi utilizzato unsigned char
), il test sarebbe fallito, perché unsigned char
non può “mantenere il valore di EOF
.
Nota inoltre che cè un secondo problema con il tuo codice. Come EOF
non è un carattere in sé, ma lo forzi in un tipo char
, molto probabilmente cè un carattere là fuori che viene interpretato erroneamente come EOF
e per metà dei caratteri possibili non è definito se verranno elaborati correttamente.
Commenti
- Coercizione per digitare
char
valori al di fuori dellintervalloCHAR_MIN
..CHAR_MAX
sarà necessario per fornire un valore definito dallimplementazione, restituisce un modello di bit che limplementazione definisce come una rappresentazione trap o genera un segnale definito dallimplementazione. Nella maggior parte dei casi, le implementazioni dovrebbero fare molto lavoro extra per fare qualcosa di diverso da due ' riduzione del complemento a s.Se le persone del Comitato per gli standard sottoscrissero lidea che i compilatori dovrebbero essere incoraggiati a implementare comportamenti coerenti con quelli della maggior parte degli altri compilatori in assenza di motivi per fare altrimenti … - … coercizione come affidabile (per non dire che il codice non dovrebbe ' t documentare le sue intenzioni, ma che
(signed char)x
dovrebbe essere considerato più chiaro e altrettanto sicuro come((unsigned char)x ^ CHAR_MAX+1))-(CHAR_MAX+1)
.) Così comè, ' non vedo alcuna probabilità che i compilatori implementino qualsiasi altro comportamento conforme agli attuali ' standard; lunico pericolo sarebbe che lo standard potesse essere modificato per interrompere il comportamento nel presunto interesse di " ottimizzazione ". - @supercat: lo standard è scritto in modo tale che nessun compilatore debba produrre codice che abbia 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 sfruttare il comportamento indefinito per fare ottimizzazioni più aggressive.
- Storicamente, lintenzione dello Standard era principalmente come tu descrivi, sebbene lo Standard descriva alcuni comportamenti in dettagli sufficienti da richiedere ai compilatori di alcune piattaforme comuni di generare più codice di quanto sarebbe richiesto con una specifica più ampia. Il tipo di coercizione in
int i=129; signed char c=i;
è uno di questi comportamenti. Relativamente pochi processori hanno unistruzione che renderebbec
uguale ai
quando ' è in lintervallo compreso tra -127 e +127 e produrrebbe qualsiasi mappatura coerente di altri valori dii
con valori compresi tra -128 e +127 diversi da due ' riduzione del complemento a s, o … - … solleverebbe costantemente un segnale in questi casi. Poiché lo standard richiede che le implementazioni producano una mappatura coerente o generino costantemente un segnale, le uniche piattaforme in cui lo standard lascerebbe spazio per qualcosa di diverso da due ' riduzione del complemento a s sarebbero cose come i DSP con hardware aritmetico saturante. Per quanto riguarda la base storica di Undefined Behavior, direi che il problema non è ' solo con piattaforme hardware. Anche su una piattaforma in cui loverflow si comporterebbe in modo molto coerente, può essere utile che un compilatore lo intrappoli …
0xff
. Memorizzazione del risultato digetchar()
in unint
risolve il problema. La tua domanda è essenzialmente la stessa della domanda 12.1 nelle comp.lang.c FAQ , che è unottima risorsa. (Inoltre,main()
dovrebbe essereint main(void)
e non ' non fa male aggiungere unreturn 0;
prima della chiusura}
.)