1

I am learning C from the book "The C Programming Language"; my question is about something I observed while trying to reformulate with few lines of code regarding input and output: why is it needed to give the getchar() function a value of a certain integer in order to have it store all the text in the input? For example with this code putchar() is printing all that I type;

int c;

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

But, why isn't it the same if I write:

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

In the latter case, for example, if I write "okok", the program is then printing "kk".

I think the reason is within some property of getchar(), that I wasn't able to get; for example, if I write a character counting program, and I want to exclude new lines, I also observed that it's working if I write it as:

int nc, c;

nc = 0;
while ((c = getchar()) != EOF)
    if (c != '\n')
        ++nc;
printf("%d", nc);

But it's not able instead to distinguish correctly the '\n' when using getchar() directly instead of c integer:

while ((c = gethar()) != EOF)
    if (getchar() != '\n')
        ++nc;
printf("%d", nc);

My purpose it is just to understand, as I wouldn't like to learn this just by memory, and in the book it is written that getchar() is working using int values, so I wonder if there is something that I missed about properties of getchar() despite reading several times, also searching in different questions in stack overflow regarding the getchar() topic.

  • 5
    The second snippet is calling `getchar()` twice in each iteration and discarding half of the characters. – Eugene Sh. Jul 30 '21 at 15:15
  • 4
    Each calls of `getchar()` consumes one character from the stdin (unless it has already reached to EOF). – MikeCAT Jul 30 '21 at 15:15
  • 1
    BTW: the fact that `getchar` returns an `int` is totally irrelevant to the question. The two preceeding commentgs are the answer you're looking for. – Jabberwocky Jul 30 '21 at 15:18
  • @MikeCAT could I ask why this happens? Is it just property of getchar() or there is a reason that is more wide? – Gregory Magnus Jul 30 '21 at 15:23
  • 1
    You cannot have it both ways. If it were the case that `getchar()` returned a character from the standard input *without* consuming it, so that your second code would not lose characters, then it would be needful to have some other mechanism to tell the program to move ahead to the next character. Without anything to cause the stream to advance, your second code would just read the same character over and over and over. – John Bollinger Jul 30 '21 at 15:24
  • This is just property of `getchar()` defined in [N1570](http://chimera.roma1.infn.it/SP/COMMON/iso-iec-9899-1990.pdf) 7.21.7.6. It reads the next character from the standard input and returns that. More generally, functions are executed every time they are called. – MikeCAT Jul 30 '21 at 15:28
  • 2
    `getchar` is an example of a function with a *side effect*. It doesn't just return a value, it *does something*, that has some kind of a global effect. `getchar`'s side effect is that it *consumes one character from the input stream* (where the input stream is an example of "global state"). Before the call to `getchar` there were N characters on the input stream; after it returns there are N-1. Or, if there are 0 characters available on the input stream when you call `getchar`, the call *blocks* until some input is available. – Steve Summit Jul 30 '21 at 15:34

2 Answers2

1

When you write

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

It always

  1. reads one character and checks it for EOF,
  2. reads one (another) character and prints it,
  3. repeats.

Therefore, in your "okok" example, the odd 'o' characters are read, compared to EOF and discarded, and only the even 'k's make it through.

If you wanted to print all of the input characters, you would have to restructure your code. For example, to call getchar() once, save the return value to a variable, then use the already read value twice - check it for EOF and print it.

while (1) {  /* loop forever (until break is called) */
    int ch = getchar();
    if (ch == EOF)
        break;
    putchar(ch);
}
Yirkha
  • 12,737
  • 5
  • 38
  • 53
  • @GregoryMagnus Don't think of `getchar` as "discarding" a character. It removes one character from the input stream, and it gives it to you, as its return value. If you store the return value in a variable, like `ch`, now you have a copy of that character, and you can do multiple things with it: check to see if it was `EOF`, print it back out with `putchar`, etc. – Steve Summit Jul 30 '21 at 16:02
  • @GregoryMagnus: A variable is used to retain the value between statements. Each time you do `getchar()`, it removes one character from the input keyboard queue and gives it back to you. So if you want to use it twice (e.g. check it and print it, as in your example), it needs to be remembered somewhere. And that's where the variable helps. – Yirkha Jul 30 '21 at 16:03
  • The only way to "lose" or "discard" a character would be if you write `getchar();` on a line by itself, without assigning its return value to a variable. Or, I guess that's not the only way, because another way would be to write `while (getchar() != EOF)`, as you originally did, because there, while you successfully check `getchar`'s return value to see that it's not `EOF`, you don't keep a copy of it, so you lose it, and when you call `getchar` a second time, you get the next character, not the one you just checked against `EOF`. – Steve Summit Jul 30 '21 at 16:03
  • @GregoryMagnus You might need a better mental model for thinking about the values returned by a function, and what happens when they're "stored in" a variable. Imagine `getchar` is a guy standing next you, reading characters from a book. He moves his finger along the book to keep track of where he is. You say, "Hey, `getchar`, give me the next character!" Imagine he writes it on a piece of paper and hands it to you. You look at it, and you see that it's not "EOF", and you *throw it away*. That's the `while (getchar() != EOF)` case. – Steve Summit Jul 30 '21 at 16:09
  • But then when you do `while ((c = getchar()) != EOF)`, it's like taking the piece of paper you got back from `getchar`, and pinning it to a bulletin board, with a label `c:` next to it. Now, you can look at the bulletin board to see if you got EOF or not, *and* when it's `putchar`'s job to print the character back out, see can look at the same piece of paper on the bulletin board, and print the same value. That value doesn't get thrown away until the next time through the loop, when you call `getchar` again and get another piece of paper and pin it to the bulletin board instead. – Steve Summit Jul 30 '21 at 16:10
  • 1
    Perfect guys, I now understand completely, I am really grateful to both, really it couldn't be more clear, only thanks to your example; I think this page can be useful to multiple people who struggle with understanding properties of this function, so truly big thanks! – Gregory Magnus Jul 30 '21 at 16:13
  • @GregoryMagnus Glad to hear you've got it. That line `while ((c = getchar()) != EOF)` is pretty inherently confusing at first, because there's a lot going on in it. See [this page](https://www.eskimo.com/~scs/cclass/krnotes/sx4f.html) for some more information on that chapter of K&R. – Steve Summit Jul 30 '21 at 16:14
  • Thanks @SteveSummit; I read the linked page and found it useful, now I able to read it correctly when it refers to avoiding two distinct calls to getchar; might refer it to it for further topic when in doubt – Gregory Magnus Jul 30 '21 at 16:32
0

getchar() function gets one char from the input and returns it.

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

You have used the getchar() function two times.

For the input 'okok'

'o' will be returned in while (getchar() != EOF). 'k' will be returned in putchar(getchar());

Since you have written putchar() here it will get the char 'k' and prints it. And again the process continues and prints only 'k'. So the output will be 'kk'

To get the value 'okok' and print it try

do
{
    char c=getchar();
    if(c!=EOF)putchar(c);
}while(c!=EOF);
Pravin
  • 13
  • 3