I read in the Dennis Ritchie's The C Programming Language book that int must be used for a variable to hold EOF – to make it sufficiently large so that it can hold EOF value – not char. But following code works fine:


main()  { 
  char c; 
  while(c!=EOF)  { 

When there is no more input, getchar returns EOF. And in the above program, the variable c, with char type, is able to hold it successfully.

Why does this work? As per the explanation in the book above mentioned, the code should not work.

  • 5
    There is no EOF character. – user7043 May 10 '13 at 9:34
  • 5
    This code is likely to fail if you read a character with the value 0xff. Storing the result of getchar() in an int solves that problem. Your question is essentially the same as question 12.1 in the comp.lang.c FAQ, which is an excellent resource. (Also, main() should be int main(void), and it wouldn't hurt to add a return 0; before the closing }.) – Keith Thompson May 10 '13 at 21:32
  • 1
    @delnan: The linked article isn't quite right about how Unix treats control-D. It doesn't close the input stream; it merely causes any fread() which is blocking on the console to return immediately with any as-yet-unread data. Many programs interpret a zero-byte return from fread() as indicating EOF, but the file will in fact remain open and able to supply more input. – supercat Aug 7 '15 at 18:10

Your code seems to work, because the implicit type conversions accidentally happen to do the right thing.

getchar() returns an int with a value that either fits the range of unsigned char or is EOF (which must be negative, usually it is -1). Note that EOF itself is not a character, but a signal that there are no more characters available.

When storing the result from getchar() in c, there are two possibilities. Either the type char can represent the value, in which case that is the value of c. Or the type char can not represent the value. In that case, it is not defined what will happen. Intel processors just chop off the high bits that don't fit in the new type (effectively reducing the value modulo 256 for char), but you should not rely on that.

The next step is to compare c with EOF. As EOF is an int, c will be converted to an int as well, preserving the value stored in c. If c could store the value of EOF, then the comparison will succeed, but if c could not store the value, then the comparison will fail, because there has been an irrecoverable loss of information while converting EOF to type char.

It seems your compiler chose to make the char type signed and the value of EOF small enough to fit in char. If char were unsigned (or if you had used unsigned char), your test would have failed, because unsigned char can't hold the value of EOF.

Also note that there is a second problem with your code. As EOF is not a character itself, but you force it into a char type, there is very likely a character out there that gets misinterpreted as being EOF and for half the possible characters it is undefined if they will be processed correctly.

  • Coercing to type char values outside the range CHAR_MIN..CHAR_MAX will is required to either yield an Implementation-Defined value, yield a bit pattern which the implementation defines as a trap representation, or raise an implementation-defined signal. In most cases, implementations would have to go through a lot of extra work to do anything other than two's-complement reduction. If people on the Standards Committee subscribed to the idea that compilers should be encouraged to implement behaviors consistent with that of most other compilers in the absence of reasons to do otherwise... – supercat Aug 6 '15 at 19:36
  • ...I would regard such coercion as being reliable (not to say that code shouldn't document its intentions, but that (signed char)x is should be considered clearer and just as safe as ((unsigned char)x ^ CHAR_MAX+1))-(CHAR_MAX+1).) As it is, I don't see any likelihood of compilers implementing any other behavior complying with today's standard; the one danger would be that the Standard might be changed to break the behavior in the supposed interest of "optimization". – supercat Aug 6 '15 at 19:42
  • @supercat: The standard is written such that no compiler has to produce code that has behaviour that is not naturally supported by the processor it targets. Most of the undefined behaviour is there because (at the time of writing the standard) not all processors behaved consistently. With the compilers getting more mature, compiler writers have started taking advantage of the undefined behaviour to make more aggressive optimisations. – Bart van Ingen Schenau Aug 7 '15 at 7:35
  • Historically, the intention of the Standard was mostly as you describe, though the Standard describes some behaviors in sufficient detail as to require compilers for some common platforms to generate more code than would be required under a looser specification. The type coercion in int i=129; signed char c=i; is one such behavior. Relatively few processors have an instruction that would make c equal i when it's in the range -127 to +127 and would yield any consistent mapping of other values of i to values in the range -128 to +127 that differed from two's-complement reduction, or... – supercat Aug 7 '15 at 14:57
  • ...would consistently raise a signal in such cases. Since the Standard requires that implementations either yield a consistent mapping or consistently raise a signal, the only platforms where the Standard would leave room for something other than two's-complement reduction would be things like DSPs with saturating-arithmetic hardware. As for the historical basis for Undefined Behavior, I would say that the issue isn't just with hardware platforms. Even on a platform where overflow would behave in a very consistent fashion, it may be useful to have a compiler trap it... – supercat Aug 7 '15 at 15:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.