Lagring af EOF (slutningen af fil) i en char-type
On februar 18, 2021 by admin Jeg læste i Dennis Ritchies The C Programming Language bog, der int
skal bruges til en variabel til at holde EOF – for at gøre den tilstrækkelig stor, så den kan holde EOF-værdi – ikke char
. Men følgende kode fungerer fint:
#include<stdio.h> main() { char c; c=getchar(); while(c!=EOF) { putchar(c); c=getchar(); } }
Når der er ikke mere input, getchar
returnerer EOF. Og i ovenstående program er variablen c
med char-typen i stand til at holde det med succes.
Hvorfor fungerer dette? I henhold til forklaringen i ovennævnte bog skal koden ikke fungere.
Kommentarer
Svar
Din kode ser ud til at fungere, fordi de implicitte typekonverteringer tilfældigvis gør det rigtige.
getchar()
returnerer en int
med en værdi, der enten passer til området unsigned char
eller er EOF
( som skal være negativ, normalt er det -1). Bemærk, at EOF
i sig selv ikke er et tegn, men et signal om, at der ikke er flere tilgængelige tegn.
Når du gemmer resultatet fra getchar()
i c
er der to muligheder. Enten kan typen char
repræsentere værdien, i hvilket tilfælde det er værdien af c
. Eller typen char
kan ikke repræsenterer værdien. I så fald er det ikke defineret, hvad der vil ske. Intel-processorer hugger bare de høje bits, der ikke passer ind i den nye type (effektivt reducerer værdien modulo 256 til char
), men det skal du ikke stole på.
Det næste trin er at sammenligne c
med EOF
. Som EOF
er en int
, c
konverteres også til en int
og bevarer også den lagrede værdi i c
. Hvis c
kunne gemme værdien af EOF
, vil sammenligningen lykkes , men hvis c
kunne ikke gemme værdien, mislykkes sammenligningen, fordi der har været et uopretteligt tab af information under konvertering af EOF
for at skrive char
.
Det ser ud til, at din kompilator valgte at lave char
typen underskrevet og værdien af EOF
lille nok til at passe ind char
. Hvis char
ikke var underskrevet (eller hvis du havde brugt unsigned char
), ville din test mislykkedes, fordi unsigned char
kan ikke holde værdien af EOF
.
Bemærk også, at der er et andet problem med din kode. Som EOF
er ikke et tegn i sig selv, men du tvinger det til en char
-type, der er meget sandsynligt et tegn derude, der bliver misfortolket som værende EOF
og for halvdelen af de mulige tegn er det udefineret, om de vil blive behandlet korrekt.
Kommentarer
- Tvingende for at skrive
char
værdier uden for områdetCHAR_MIN
..CHAR_MAX
vil kræves for at enten give en implementeringsdefineret værdi, giver et bitmønster, som implementeringen definerer som en fældrepræsentation, eller hæver et implementeringsdefineret signal. I de fleste tilfælde bliver implementeringer nødt til at gå igennem en masse ekstra arbejde for at gøre noget andet end to ' s-komplementreduktion.Hvis folk i Standardkomiteen tilsluttede sig tanken om, at kompilatorer skulle tilskyndes til at implementere adfærd, der er i overensstemmelse med de fleste andre compilers, i mangel af grunde til at gøre andet … - … Jeg ville betragte sådan tvang som pålidelig (for ikke at sige, at koden ikke ' t skal dokumentere sine intentioner, men at
(signed char)x
er, bør betragtes som klarere og lige så sikkert som((unsigned char)x ^ CHAR_MAX+1))-(CHAR_MAX+1)
.) Som det er, ser jeg ' ikke sandsynligheden for, at kompilatorer implementerer anden adfærd, der overholder i dag ' s standard; den ene fare ville være, at standarden kunne ændres for at bryde adfærden i den påståede interesse for " optimering ". - @supercat: Standarden er skrevet således, at ingen compiler skal producere kode, der har adfærd, der ikke naturligt understøttes af den processor, den målretter mod. Det meste af den udefinerede adfærd er der, fordi (på tidspunktet for skrivning af standarden) ikke alle processorer opførte sig konsekvent. Efterhånden som kompilatorerne bliver mere modne, er kompilatorforfattere begyndt at udnytte den udefinerede adfærd for at foretage mere aggressive optimeringer.
- Historisk var standardens hensigt mest som du beskriver, selvom standarden beskriver nogle adfærd i tilstrækkelig detalje til at kræve, at kompilatorer til nogle almindelige platforme genererer mere kode, end der ville være krævet under en løsere specifikation. Typet tvang i
int i=129; signed char c=i;
er sådan en adfærd. Relativt få processorer har en instruktion, der ville gørec
ligi
når det ' er i området -127 til +127 og ville give en ensartet kortlægning af andre værdier afi
til værdier i området -128 til +127, der afveg fra to ' s-komplementreduktion, eller … - … ville i sådanne tilfælde konsekvent hæve et signal. Da standarden kræver, at implementeringer enten giver en konsistent kortlægning eller konsekvent hæver et signal, ville de eneste platforme, hvor standarden ville give plads til noget andet end to ' s-komplementreduktion, være ting som DSPer med mættende aritmetisk hardware. Hvad det historiske grundlag for udefineret adfærd angår, vil jeg sige, at problemet ikke er ' t kun med hardwareplatforme. Selv på en platform, hvor overløb vil opføre sig meget konsekvent, kan det være nyttigt at have en compiler til at fælde det …
0xff
. Lagring af resultatet afgetchar()
i enint
løser dette problem. Dit spørgsmål er stort set det samme som spørgsmål 12.1 i comp.lang.c FAQ , hvilket er en fremragende ressource. (Også,main()
skal væreint main(void)
, og det ville ikke ' være ondt at tilføje enreturn 0;
før den afsluttende}
.)