[...] read also says how many bytes of the file were returned, so end of file is assumed when a read says "zero bytes are being returned." [...]

When a program reads from your terminal, each input line is given to the program by the kernel only when you type its newline (i.e, press RETURN). [...]

[...]

Now try something different: type some characters and then a *ctl*-d rather than a RETURN:

$ cat -u
123
ctl-d123

cat prints the characters out immediately. *ctl*-d says, "immediately send the characters I have typed to the program that is reading from my terminal." The *ctl*-d itself is not sent to the program, unlike a newline. Now type a second ctl-d, with no other chracters:

$ cat -u
123
ctl-d123ctl-d$

The shell responds with a prompt, because cat read no characters, decided that meant end of file, and stopped. ctl-d sends whatever you have typed to the program that is reading from the terminal. If you haven't typed anything, the program will therefore read no characters, and that looks like the end of the file. That is why *ctl*-d logs you out --- the shell sees no more input. Of course, *ctl*-d is usually used to signal an end-of-file but it is interesting that is has a more general function.

Brian W. Kernighan, Rob Pike. The UNIX Programming Environment. Prentice-Haskell Software Series, 1984, section 2.1, pages 44-45.