0

This first K&R example of character io has kind of stumped me.

#include <stdio.h>
/* copy input to output; 2nd version */
main()
{
  int c;
  while ((c = getchar()) != EOF)
    putchar(c);
}

For example entering abcd will print abcd. I would expect it to only print a because the string from stdin is never stored in full inside this program, and even if it was nothing here would iterate through it.

I don't understand is how getchar() is iterating through stdin to get to the next character. Is this a function of stdin or C?

GBE
  • 19
  • 4
  • 1
    That's a good question, you could think of stdin like a temporary file. You write to it, and `getchar()` reads from it. The input buffer is stored somewhere, and `getchar()` reads one character from it, and advances the reading pointer by one. – malkaroee May 17 '22 at 11:03
  • @malkaroee Thanks! I searched for reading pointers and found [IBM docs](https://www.ibm.com/docs/en/i/7.3?topic=functions-getc-getchar-read-character) that say `getchar()` reads then advances the stream position, and a [windows doc](https://learn.microsoft.com/en-us/dotnet/api/system.io.stream?view=net-6.0) that painted a decent picture of how the stream is accessed. I tested this iteration functionality by writing getchar and putchar in a sequence N times outside of a loop and as expected it printed the string to N characters. Wild! – GBE May 17 '22 at 11:42
  • `getchar` **is** iteration – user253751 May 17 '22 at 12:02
  • @Lundin *a function `main()` without `return` is non-conforming in standard C.* What are you talking about? A `main` function without `return` has been [perfectly legal in all versions of C since C99](https://stackoverflow.com/questions/13545291/can-i-omit-return-from-main-in-c). – Steve Summit May 17 '22 at 12:24
  • @SteveSummit If you are going to argue about it, cite proper sources. ISO 9899:1990 6.6.6.4 "if a return statement without an expression is executed and the value of the function call is used by the caller, the behavior is undefined. Reaching the `}` that terminates a function is equivalent to executing a return statement without an expression." – Lundin May 17 '22 at 12:30
  • @Lundin There is an explicit exception for a function named `main`, as extensively cited in the [question I linked to](https://stackoverflow.com/questions/13545291/can-i-omit-return-from-main-in-c). – Steve Summit May 17 '22 at 12:55
  • @SteveSummit Yes in C99 and beyond. But K&R wasn't written for C99 and `main()` will not compile in C99. So it was always wrong no matter how you put it. – Lundin May 17 '22 at 12:56
  • @Lundin Agree to disagree, then. – Steve Summit May 17 '22 at 12:59

2 Answers2

2

getchar reads from a stream. That's what stdin is. A stream is a data structure which represents a sequence of data being read from or written to somewhere. A stream is not like a single variable. It is not like an array. It is not something you "iterate through". It is something you get from (or put to).

You might think of it like a deck of cards that you're using in a game. That is, you can do operations like: look at the card on the top of the deck, take the card from the top of the deck and put it in your hand, take N cards from the top of the deck and put them in your hand.

So when you see code like

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

using the deck-of-cards analogy you can think of this as

while ( I try to take a card from the top of the deck and I do get one )
    put the card in my hand;

Typically you can take quite a few cards. Eventually, though, the deck will be all used up, and your loop will stop.

Calling getchar is not like calling sqrt(4). If you call sqrt(4) multiple times, you get the same answer every time. But if you call getchar() multiple times, you get a different answer every time. (Formally, we say that getchar has state, and that calling it has a side effect. Specifically, each time you call getchar, it has the side effect of permanently removing one character from the stream, that is, of updating the state of the stream to record the fact that one more character has been read.)

P.S. The deck-of-cards analogy is not perfect. With a conventional data stream, there is no way to do something like taking a card from the middle of the deck, or shuffling the deck. But you can, typically, put one card back on top of the deck (this is called ungetc in C). Also, down inside, as the deck gets low, if you're reading from a file, the stdio machinery can grab a bunch of new cards (another block from the file) and stick them in at the bottom of the deck.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
-1

You enter "abcd"

It reads 'a', checks if it is not equal to EOF. 
If not prints 'a'
Then you go to the beginning of the loop:
It reads 'b', checks if it is not equal to EOF. 
If not prints 'b'
Then you go to the beginning of the loop:
.....

It returns EOF, checks if it is not equal to EOF. 
As it is equal it terminates the wile loop

0___________
  • 60,014
  • 4
  • 34
  • 74
  • 3
    `getchar()` does *not* "read" EOF. `getchar` detects that the stream has been exhausted and returns EOF to indicate that you have reached the end of the file. – William Pursell May 17 '22 at 11:27
  • @WilliamPursell That's splitting hairs. The C standard says "If the stream is at end-of-file, the end-of-file indicator for the stream is set and getchar returns EOF." – Lundin May 17 '22 at 11:32
  • 1
    @Lundin It's not splitting hairs at all. There is a mythology floating around that EOF is a symbol that is actually present in streams. "getchar returns EOF" is *very* different than "getchar reads EOF", and I am glad to see that the answer was edited to remove that phrase. – William Pursell May 17 '22 at 11:37
  • 1
    @WilliamPursell Streams is a pretty mythological concept to begin with, it's an abstraction layer above a plain input buffer. The C standard does not mention how the library functions can know that a stream is empty or not. Very likely by reading a size counter, but that isn't specified. – Lundin May 17 '22 at 11:45