Het EOF-teken (End of File) opslaan in een char-type
Geplaatst op februari 18, 2021 door admin Ik las in de The C Programming Language van Dennis Ritchie boek dat int
moet worden gebruikt om een variabele EOF te laten bevatten – om het voldoende groot te maken zodat het EOF-waarde – niet char
. Maar de volgende code werkt prima:
#include<stdio.h> main() { char c; c=getchar(); while(c!=EOF) { putchar(c); c=getchar(); } }
Wanneer er is geen invoer meer, getchar
geeft EOF terug. En in het bovenstaande programma kan de variabele c
, met het char-type, deze vasthouden succesvol.
Waarom werkt dit? Volgens de uitleg in het bovenstaande boek, zou de code niet moeten werken.
Reacties
Antwoord
Uw code lijkt te werken, omdat de impliciete typeconversies per ongeluk het juiste doen.
getchar()
geeft een int
terug met een waarde die ofwel past binnen het bereik van unsigned char
of EOF
( die negatief moet zijn, meestal is het -1). Merk op dat EOF
zelf geen teken is, maar een signaal dat er geen tekens meer beschikbaar zijn.
Bij het opslaan van het resultaat van getchar()
in c
zijn er twee mogelijkheden. Ofwel het type char
kan de waarde vertegenwoordigen, in welk geval dat de waarde is van c
. Of het type char
kan niet de waarde vertegenwoordigen. In dat geval staat niet vast wat er gaat gebeuren. Intel-processors hakken gewoon de hoge bits weg die niet in het nieuwe type passen (waardoor de waarde modulo 256 effectief wordt verlaagd voor char
), maar daar moet je niet op vertrouwen.
De volgende stap is om c
te vergelijken met EOF
. Zoals EOF
is een int
, c
wordt ook geconverteerd naar een int
, waarbij de opgeslagen waarde behouden blijft in c
. Als c
de waarde van EOF
zou kunnen opslaan, zal de vergelijking slagen , maar als c
de waarde niet zou kunnen opslaan, mislukt de vergelijking, omdat er een onherstelbaar verlies van informatie is opgetreden tijdens het converteren van EOF
om char
te typen.
Het lijkt erop dat je compiler ervoor heeft gekozen om het char
type ondertekend en de waarde van EOF
klein genoeg om in char
te passen. Als char
niet ondertekend was (of als u unsigned char
had gebruikt), zou uw test zijn mislukt, omdat unsigned char
kan “de waarde van EOF
niet bevatten.
Merk ook op dat er een tweede probleem is met uw code. Zoals EOF
is zelf geen karakter, maar je forceert het in een char
type, er is zeer waarschijnlijk een karakter dat verkeerd wordt geïnterpreteerd als EOF
en voor de helft van de mogelijke karakters is het niet gedefinieerd of ze correct verwerkt zullen worden.
Reacties
- Dwang om
char
waarden te typen buiten het bereikCHAR_MIN
..CHAR_MAX
is vereist voor beide een door implementatie gedefinieerde waarde, een bitpatroon opleveren dat door de implementatie wordt gedefinieerd als een trap-weergave, of een door de implementatie gedefinieerd signaal genereren. In de meeste gevallen zouden implementaties doe veel extra werk om iets anders te doen dan twee ' s-complement reducties.Als mensen van de normcommissie het idee zouden onderschrijven dat samenstellers zouden moeten worden aangemoedigd om gedrag te implementeren dat consistent is met dat van de meeste andere samenstellers bij afwezigheid van redenen om anders te doen … - … zou ik dat beschouwen dwang als betrouwbaar (om niet te zeggen dat code zijn bedoelingen niet ' moet documenteren, maar dat
(signed char)x
moet worden beschouwd als duidelijker en net zo safe als((unsigned char)x ^ CHAR_MAX+1))-(CHAR_MAX+1)
.) Zoals het is, zie ik ' geen enkele kans dat compilers enig ander gedrag implementeren dat voldoet aan de huidige ' s standaard; het enige gevaar zou zijn dat de standaard zou worden gewijzigd om het gedrag te breken in het vermeende belang van " optimalisatie ". - @supercat: De standaard is zo geschreven dat geen enkele compiler code hoeft te produceren die gedrag vertoont dat niet van nature wordt ondersteund door de processor waarop hij zich richt. Het meeste ongedefinieerde gedrag is aanwezig omdat (op het moment van schrijven van de standaard) niet alle processors zich consistent gedroegen. Nu de compilers volwassener worden, beginnen compilerschrijvers te profiteren van het ongedefinieerde gedrag om agressievere optimalisaties te maken.
- Historisch gezien was de bedoeling van de standaard grotendeels zoals u beschrijft, hoewel de standaard sommige gedragingen beschrijft in voldoende details om compilers voor sommige gangbare platforms te verplichten om meer code te genereren dan nodig zou zijn onder een lossere specificatie. Het type dwang in
int i=129; signed char c=i;
is zon gedrag. Relatief weinig processors hebben een instructie diec
gelijk zou maken aani
wanneer deze ' s in het bereik -127 tot +127 en zou een consistente toewijzing opleveren van andere waarden vani
aan waarden in het bereik -128 tot +127 die verschilden van twee ' s-complement reductie, of … - … zou in dergelijke gevallen consequent een signaal geven. Aangezien de standaard vereist dat implementaties een consistente mapping opleveren of consequent een signaal genereren, zijn de enige platforms waar de standaard ruimte laat voor iets anders dan twee ' s-complement reductie dingen zoals DSPs met verzadigende rekenkundige hardware. Wat betreft de historische basis voor ongedefinieerd gedrag, zou ik zeggen dat het probleem niet ' t alleen met hardwareplatforms is. Zelfs op een platform waar overflow zich op een zeer consistente manier zou gedragen, kan het handig zijn om een compiler het te laten vangen …
0xff
. Het resultaat opslaan vangetchar()
in eenint
lost dat probleem op. Uw vraag is in wezen hetzelfde als vraag 12.1 in de comp.lang.c FAQ , wat een uitstekende bron is. (Bovendien zoumain()
int main(void)
moeten zijn, en het zou ' geen pijn doen om eenreturn 0;
vóór de afsluitende}
.)