Lagring av EOF (End of File) -tecken i en char-typ
On februari 18, 2021 by admin Jag läste i Dennis Ritchies The C Programming Language bok att int
måste användas för att en variabel ska hålla EOF – för att göra den tillräckligt stor så att den kan hålla EOF-värdet – inte char
. Men följande kod fungerar bra:
#include<stdio.h> main() { char c; c=getchar(); while(c!=EOF) { putchar(c); c=getchar(); } }
När det finns är inte mer ingång, getchar
returnerar EOF. Och i ovanstående program kan variabeln c
, med char-typ, hålla den framgångsrikt.
Varför fungerar det? Enligt förklaringen i boken ovan ska koden inte fungera.
Kommentarer
Svar
Din kod verkar fungera, eftersom de implicita typkonverteringarna av misstag råkar göra rätt.
getchar()
returnerar ett int
med ett värde som antingen passar intervallet unsigned char
eller är EOF
( som måste vara negativ, vanligtvis är det -1). Observera att EOF
i sig inte är ett tecken utan en signal om att det inte finns fler tecken tillgängliga.
När du lagrar resultatet från getchar()
i c
finns det två möjligheter. Antingen typen char
kan representera värdet, i vilket fall det är värdet för c
. Eller typen char
kan inte representera värdet. I så fall är det inte definierat vad som kommer att hända. Intel-processorer hugger bara av de höga bitarna som inte passar in i den nya typen (effektivt minskar värdet modulo 256 för char
), men du borde inte lita på det.
Nästa steg är att jämföra c
med EOF
. Eftersom EOF
är en int
, c
kommer att konverteras till en int
, vilket också sparar det lagrade värdet i c
. Om c
kunde lagra värdet på EOF
, kommer jämförelsen att lyckas , men om c
inte kunde lagra värdet, kommer jämförelsen att misslyckas, eftersom det har skett en oåterkallelig förlust av information när du konverterar EOF
för att skriva char
.
Det verkar som att din kompilator valde att göra char
-typ signerad och värdet på EOF
litet tillräckligt för att passa in i char
. Om char
inte var signerade (eller om du hade använt unsigned char
) skulle ditt test ha misslyckats, eftersom unsigned char
kan inte hålla värdet på EOF
.
Observera också att det finns ett andra problem med din kod. Som EOF
är inte en karaktär i sig, men du tvingar den till en char
-typ, det finns mycket troligt en karaktär där ute som tolkas felaktigt som EOF
och för hälften av de möjliga tecknen är det odefinierat om de kommer att bearbetas korrekt.
Kommentarer
- Tvingande för att skriva
char
värden utanför intervalletCHAR_MIN
..CHAR_MAX
krävs krävs för att antingen ge ett Implementationsdefinierat värde, ge ett bitmönster som implementeringen definierar som en fällrepresentation, eller höja en implementeringsdefinierad signal. I de flesta fall måste implementeringar gå igenom mycket extra arbete för att göra något annat än två ' s-komplementreduktion.Om personer i standardkommittén prenumererar på idén att kompilatorer ska uppmuntras att genomföra beteenden som överensstämmer med de flesta andra kompilatorers i avsaknad av skäl att göra annat … - … tvång som tillförlitlig (för att inte säga att koden inte ska ' t dokumentera sina avsikter, men att
(signed char)x
är bör betraktas tydligare och precis som säkert som((unsigned char)x ^ CHAR_MAX+1))-(CHAR_MAX+1)
.) Som det är, ser jag ' inte sannolikheten för att kompilatorer implementerar något annat beteende som följer idag ' s standard; den enda faran skulle vara att standarden kan ändras för att bryta beteendet i det förmodade intresset för " optimering ". - @supercat: Standarden är skriven så att ingen kompilator behöver producera kod som har beteende som inte naturligt stöds av processorn den riktar sig till. Det mesta av det odefinierade beteendet finns eftersom (vid skrivandet av standarden) inte alla processorer uppförde sig konsekvent. Med att kompilatorerna blir mogenare har kompilatorförfattare börjat utnyttja det odefinierade beteendet för att göra mer aggressiva optimeringar.
- Historiskt var standardens avsikt mestadels som du beskriver, även om standarden beskriver vissa beteenden i tillräcklig detalj för att kräva kompilatorer för vissa vanliga plattformar för att generera mer kod än vad som skulle krävas enligt en lösare specifikation. Typen tvång i
int i=129; signed char c=i;
är ett sådant beteende. Relativt få processorer har en instruktion som skulle görac
likai
när det ' är i intervallet -127 till +127 och skulle ge någon konsekvent mappning av andra värden påi
till värden i intervallet -128 till +127 som skilde sig från två ' s-komplementreduktion, eller … - … skulle konsekvent höja en signal i sådana fall. Eftersom standarden kräver att implementeringar antingen ger en konsekvent mappning eller konsekvent höjer en signal, skulle de enda plattformarna där standarden skulle ge utrymme för något annat än två ' s-komplementreduktion vara saker som DSP med mättande aritmetisk hårdvara. När det gäller den historiska grunden för odefinierat beteende skulle jag säga att frågan inte bara är ' med hårdvaruplattformar. Även på en plattform där överflöd skulle bete sig på ett mycket konsekvent sätt kan det vara användbart att ha en kompilator som fäller den …
0xff
. Lagrar resultatet avgetchar()
i enint
löser problemet. Din fråga är i stort sett densamma som fråga 12.1 i comp.lang.c FAQ , vilket är en utmärkt resurs. (main()
borde också varaint main(void)
, och det skulle ' inte skada att lägga till enreturn 0;
före den avslutande}
.)