Stocarea caracterului EOF (End of File) într-un tip de caractere
On februarie 18, 2021 by admin Am citit în Limbajul de programare C al lui Dennis Ritchie ” carte care int
trebuie folosită pentru ca o variabilă să dețină EOF – pentru a o face suficient de mare încât să poată deține valoarea EOF – nu char
. Dar următorul cod funcționează bine:
#include<stdio.h> main() { char c; c=getchar(); while(c!=EOF) { putchar(c); c=getchar(); } }
Când există nu mai este intrare, getchar
returnează EOF. Și în programul de mai sus, variabila c
, cu tipul char, este capabilă să o mențină cu succes.
De ce funcționează acest lucru? Conform explicațiilor din cartea menționată mai sus, codul nu ar trebui să funcționeze.
Comentarii
Răspuns
Codul dvs. pare să funcționeze, deoarece conversiile implicite de tip se întâmplă accidental să facă ceea ce trebuie.
getchar()
returnează un int
cu o valoare care se potrivește fie cu unsigned char
, fie este EOF
( care trebuie să fie negativ, de obicei este -1). Rețineți că EOF
în sine nu este un caracter, ci un semnal că nu mai sunt caractere disponibile.
Când se stochează rezultatul de la getchar()
în c
, există două posibilități. Fie tipul char
poate reprezenta valoarea, caz în care aceasta este valoarea c
. Sau tipul char
nu poate reprezintă valoarea. În acest caz, nu este definit ce se va întâmpla. Procesoarele Intel tocă doar biții mari care nu se încadrează în noul tip (reducând efectiv valoarea modulo 256 pentru char
), dar nu ar trebui să vă bazați pe asta.
Următorul pas este să comparați c
cu EOF
. Deoarece EOF
este un int
, c
va fi convertit și într-un int
, păstrând valoarea stocată în c
. Dacă c
ar putea stoca valoarea EOF
, atunci comparația va reuși , dar dacă c
ar putea nu stoca valoarea, atunci comparația va eșua, deoarece a existat o pierdere irecuperabilă de informații în timpul conversiei EOF
pentru a tasta char
.
Se pare că compilatorul dvs. a ales să facă tipul char
semnat și valoarea EOF
mică suficient pentru a se încadra în char
. Dacă char
nu ar fi fost semnat (sau dacă ați fi folosit unsigned char
), testul dvs. ar fi eșuat, deoarece unsigned char
nu poate conține valoarea EOF
.
Rețineți că există o a doua problemă cu codul dvs. Ca EOF
nu este un personaj în sine, dar îl forțezi într-un tip char
, este foarte probabil un personaj acolo care să fie interpretat greșit ca fiind EOF
și pentru jumătate din caracterele posibile este nedefinit dacă vor fi procesate corect.
Comentarii
- Coercing pentru a tasta
char
valori în afara intervaluluiCHAR_MIN
..CHAR_MAX
va fi necesar pentru a produce o valoare definită de implementare, produce un model de biți pe care implementarea îl definește ca reprezentare capcană sau crește un semnal definit de implementare. În majoritatea cazurilor, implementările ar trebui să treceți printr-o mulțime de lucruri suplimentare pentru a face orice altceva decât reducerea complementului '.Dacă persoanele din Comitetul pentru standarde s-au înscris la ideea că compilatoarele ar trebui încurajate să implementeze comportamente compatibile cu cele ale majorității celorlalți compilatori, în absența unor motive pentru a face altfel … - … aș considera astfel constrângerea ca fiind fiabilă (ca să nu spunem că codul nu ar trebui să ocumenteze ' t documenta intențiile sale, ci că
(signed char)x
este considerat mai clar și la fel de clar sigur ca((unsigned char)x ^ CHAR_MAX+1))-(CHAR_MAX+1)
.) Așa cum este, nu ' nu văd nicio probabilitate ca compilatoarele să implementeze orice alt comportament care respectă astăzi ' s standard; singurul pericol ar fi că standardul ar putea fi schimbat pentru a rupe comportamentul în interesul presupus al " optimizare ". - @supercat: Standardul este scris astfel încât niciun compilator să nu producă cod care are un comportament care nu este susținut în mod natural de procesorul pe care îl vizează. Cea mai mare parte a comportamentului nedefinit este acolo deoarece (la momentul redactării standardului) nu toți procesoarele s-au comportat în mod consecvent. Odată cu compilatorii din ce în ce mai maturi, scriitorii de compilatori au început să profite de comportamentul nedefinit pentru a face optimizări mai agresive.
- Din punct de vedere istoric, intenția standardului era în mare parte așa cum ați descris, deși standardul descrie unele comportamente în detalii suficiente pentru a necesita compilatoare pentru unele platforme comune pentru a genera mai mult cod decât ar fi necesar în conformitate cu o specificație mai slabă. Tipul de constrângere din
int i=129; signed char c=i;
este un astfel de comportament. Relativ puține procesoare au o instrucțiune care ar facec
egali
atunci când ' s intervalul -127 până la +127 și ar produce orice mapare consecventă a altor valori alei
la valorile din intervalul -128 până la +127 care difereau de două ' reducerea complementului s sau … - … ar ridica în mod constant un semnal în astfel de cazuri. Deoarece standardul impune ca implementările să producă o mapare consistentă sau să ridice în mod constant un semnal, singurele platforme în care standardul ar lăsa loc pentru altceva decât două reduceri de completare ' ar fi lucrurile ca DSP-uri cu hardware aritmetic saturant. În ceea ce privește baza istorică a Comportamentului nedefinit, aș spune că problema nu este ' doar cu platformele hardware. Chiar și pe o platformă în care depășirea s-ar comporta într-un mod foarte consecvent, poate fi util ca un compilator să-l capteze …
0xff
. Stocarea rezultatuluigetchar()
într-unint
rezolvă această problemă. Întrebarea dvs. este în esență aceeași cu întrebarea 12.1 din comp.lang.c FAQ , care este o resursă excelentă. (De asemenea,main()
ar trebui să fieint main(void)
și ' nu ar fi rău să adăugați unreturn 0;
înainte de închidere}
.)