4

I'm in front of something I can't explain. There is no better way of explaining than giving an example :

#include <stdio.h>

int main ()
{
    char c;
    while (1) {
        c = getchar();
        printf("%x\n", c);
    }
   return(0);
}

If I execute this command, it just iterates indefinitely this way:

$ echo -n "A" | ./binary
41
ffffffff
ffffffff
ffffffff
ffffffff
...

From what I know and what I read (Confusion about how a getchar() loop works internally), I thought echo -n "A" would send a A to stdin and then trigger an EOF event (I'm not sure what EOF really is) once, thus my loop would iterate twice at most, and then would sleep waiting for a new input in stdin.

But no, it iterates over EOF, and I don't understand why.

I ran this command to try to understand :

$ echo -n "A" | strace ./binary
read(0, "A", 4096)                      = 1
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 4), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7ff6000
write(1, "41\n", 341
)                     = 3
read(0, "", 4096)                       = 0
write(1, "ffffffff\n", 9ffffffff
)               = 9
read(0, "", 4096)                       = 0
write(1, "ffffffff\n", 9ffffffff
)               = 9
...

So it appears read() is reading nothing, and returns 0, which is interpreted by getchar() as EOF. But why, oh why does it iterate like this, whereas when I execute this binary the normal way, it works as expected :

$ ./binary
A
41
a
B
42
a
^C
$

(Above output could be a bit confusing, but when I entered A then Return, I sent A then \n (0x0a) to stdin, so the binary just displays these in their hexa representations ,41 and a)

Could someone explain this to me ? What did I miss ?

Thanks a lot for reading !

Community
  • 1
  • 1
pixis
  • 2,761
  • 1
  • 11
  • 26

3 Answers3

5

Once you encounter EOF, getchar will return immediately with return value EOF. The stream pointer for the stream will not advance, it will stay on the EOF marker. Subsequent calls to getchar will also return immediately, since the stream is still at the EOF marker.

Note that this differs from the behaviour when stdin is attached to an input device. In that case, getchar will pause, waiting for further input if the input buffer is empty. getcharwill not return EOF until an EOF is sent from the input device (CTRL-D sends EOF from a keyboard in Linux).

Klas Lindbäck
  • 33,105
  • 5
  • 57
  • 82
  • Ok that's helpful ! Thanks for that answer, accepted :) – pixis Aug 06 '15 at 13:19
  • 2
    `char c` will never compare equal to `EOF` if `char` is unsigned. And it will equal `EOF` if `char` is signed for certain non-ASCII chars (those which will produce the corresponding negative value as `EOF`). – too honest for this site Aug 06 '15 at 14:04
5

getchar() returns an int, not a char. The larger return value allows you to look for a return value EOF, which is -1. This is the return value for errors and end of file. Changing the type of c to int will not change the behavior of printf.

#include <stdio.h>

int main ()
{
    int c;
    while (1) {
        c = getchar();
        if ( c == EOF) {
            break;
        }
        printf("%x\n", c);
    }
   return(0);
}
Robert Jacobs
  • 3,266
  • 1
  • 20
  • 30
2

First, EOF is -1 which is 0xFFFFFFFF.

the system function 'getchar()' returns an int, not a char.

So to make any comparison, 'c' must be an int, not a char.

Now, to exit a while loop, there must be a comparison to some condition that must be true to continue in the loop.

suggest using the following code model.

#include <stdio.h>

int main ( void )  //<< for main, better to use 'void' rather than empty braces
{
    int c; //<< because getchar() returns an int, not a char

    // following loop has EOF as exit condition
    // note in C, && is lower presidence than = and != so is evaluated last
    // note in C, && is evaluated from left to right
    // note to help the compiler to catch errors,
    //      always place the literal on the left of a comparison
    while (c = getchar() && EOF != c) 
    {
        printf("%x\n", c);
    }
   return(0);
} // end function: main
user3629249
  • 16,402
  • 1
  • 16
  • 17