2

I have looked around the site regarding this K&R example and the answers seem to revolve around 'why is this a type int or what is EOF?' kinda guys. I believe that I understand those. It's the results that I don't understand. I had expected this code to take a single character, print it and then wait for another character or EOF.

The results that I see are the input waiting until I press return, then everything that I typed shows up and the more waiting for input.

Is the while loop just 'looping' until I end the text stream with the carrage return and then shows what putchar(c) has been hiding somewhere?

The code is:

#include <stdio.h>

/* copy input to output: 1st version */
main()
{
    int c;

    c = getchar();
    while(c != EOF) {
        putchar(c);
        c = getchar();
    }  
}

Now, if I sneak a putchar(c) before on the line just before the while, I sort of get what I expected. I still must enter a text stream and press return. The result is the first character of the stream and the program exits.

Evidently there is a big picture gap for me going on.

Thank you for your help

Leandros
  • 16,805
  • 9
  • 69
  • 108
  • 2
    Don't use K&R coding style, it is deprecated. C has evolved the last >17 years. About your text: not sure what your **specific** question is. This is not a tutorial site. – too honest for this site Apr 18 '16 at 19:01
  • 5
    That's because standard input and output are buffered: The characters you entered are stored internally until you hit enter and are then processed in batch. Likewise, the output characters are stored until you print a new-line character, at which time everything is flushed. If you want a direct correspondence between input and output, you must use special libraries instead of console I/O. – M Oehm Apr 18 '16 at 19:01
  • 1
    And `CR` (aka carriage return) does not terminate a stream. – too honest for this site Apr 18 '16 at 19:02
  • 1
    Crystal ball says that you don't know how to generate EOF with the keyboard. You don't have to disconnect your keyboard. If you use Linux or OSX then type Ctrl+D. If you use Windows then type Ctrl+Z. Try redirecting input to make it a bit more clearer/useful. – Hans Passant Apr 18 '16 at 19:02
  • 1
    @Olaf The book is still very much relevant. And I do think it's a valid question, even though it's explained in the book. – Leandros Apr 18 '16 at 19:05
  • @MOehm: `stdout` is supposed to be flushed when you read from `stdin`. – EOF Apr 18 '16 at 19:06
  • 1
    @Leandros: I will not discuss the didactics. But it teaches ancient, partly non-standard and deprecated C coding style. Also the question is badly researched and unclear what OPs actual problem is. – too honest for this site Apr 18 '16 at 19:07
  • @EOF: Okay, but the read only happens after return was pressed. The effect is that you get alternate lines of input and output. – M Oehm Apr 18 '16 at 19:07
  • 2
    @Olaf The first version teaches the old, pre-standard C, that is correct. It should be avoided. The second version teaches ANSI-C, which is still pretty much relevant. A good amount of C is still written in ANSI-C. – Leandros Apr 18 '16 at 19:10
  • 2
    [Suffering From Buffering](http://perl.plover.com/FAQs/Buffering.html) may help. – Schwern Apr 18 '16 at 19:10
  • @Leandros: If I meant the first edition, I'd written >27 years. C90 is also ancient and has some compatibility issues with C99 (wish there were more, they should have thrown much more old russish over board). – too honest for this site Apr 18 '16 at 19:14
  • See https://www.eskimo.com/~scs/cclass/krnotes/sx4f.html. – Steve Summit Apr 18 '16 at 19:20
  • 1
    @Olaf Well, yes, it's old. But not obsolete. A ton of old and new C code is written in C90, probably most notably the Linux Kernel. It's also well kept alive by Microsoft refusing to support anything later than C90. But We shouldn't discuss this here, I agree. Sorry. But I still curious why C90 should have compatibility issues with C99? Isn't every C90 code also valid C99? – Leandros Apr 18 '16 at 19:22
  • I can only think of one, with usage of `restrict` pointers. Violating strict aliasing in C90, but not in C99 with the restrict keyword. But that's not strictly the same program. – Leandros Apr 18 '16 at 19:30
  • @Leandros the claim about Linux kernel being written in C90 is just plain incorrect. Linux is post-C99, but isn't even C99-compliant, it requires GNU extensions compile. – Antti Haapala -- Слава Україні Apr 01 '19 at 07:04

2 Answers2

2

By default, stdin and stdout are buffered. That means that they save up batches of characters and send them at once for efficiency. Typically, the batch is saved up until there's no more room in the buffer or until there's a newline or EOF in the stream.

When you call getchar(), you're asking from characters from stdin. Supposed you type A, that character is saved in the buffer and then the system waits for more input. If you type B, that character goes into the buffer next. Perhaps after that, you hit Enter, and a newline is put in the buffer. But the newline also interrupts the buffering process, so the original call to getchar() returns the first character in the buffer (A). On the next iteration, you call getchar() again, and it immediately returns the next character in the buffer (B). And so on.

So it's not that your while loop is running until you end the line, it's that the first call to getchar() (when the buffer is empty) is waiting until it has either a full buffer or it has seen a newline.

When you interleave output functions, like putchar(), most C runtime libraries will "flush" stdin when you do something that sends data to stdout (and vice versa). (The intent is to make sure the user sees a prompt before the program waits for input.) That's why you started seeing different behavior when you added the putchar() calls.

You can manually flush a buffer using the flush() function. You can also control the size of the buffer used by the standard streams using setvbuf().

As Han Passant pointed out in the comments, a newline doesn't "terminate the stream." To get an EOF on stdin, you have to type Ctrl+D (or, on some systems, Ctrl+Z). An EOF will also flush the buffer. If you've redirected a file or the output from another program to stdin, the EOF will happen once that input is exhausted.

While it's true that K&R C is very old, and even ANSI C isn't as common today as it was, everything about buffering with stdin and stdout is effectively the same in the current standards and even in C++. I think the only significant change is that the C standards now explicitly call out the desirability of having stdin and stdout cause the other to flush.

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
0

I appreciate your answer, and the buffering as you describe is very helpful and interesting.

Evidently, I also must have mis-read/understood, K&R. They define a text stream as ". . . consists of zero or more characters followed by a new line character," which I took to mean the return/enter key; ending it, and then allowing output.

Also, I would like to thank all of you who offered helpful comments.

By the way, I clearly understood that I had to enter ^D to generate EOF, which terminates the program. I appreciate that you are all top level programmers, and thank you for your time. I guess that I will need to find another place to discuss what the text that R&R wrote regarding this exercise is all about.