1

I have trouble understanding getchar() and EOF. I was trying to run this code:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    char c;
    int a = 0; //no. of characters
    while (1) {
        c = getchar();
        if (c == EOF) {
            // printf("%i",c); 
            break;
        }
        putchar(c);
        ++a;
    }
    printf("%i", a);
    int b;
    while ((b = getchar()) != EOF) {
        putchar(b);
    }
    printf("here"); // to check wether the code written after the loop is executed
}

I terminated the first loop by pressing Ctrl-D twice, I found many posts explaining the reason for this. But whenever I try to call the getchar() function after the first loop it keeps returning EOF, even though that would have been already read by the last call in the first loop.

Code editor - VSCode

OS - macOS

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    Re “EOF … that would have been already read”: You do not “read” an end-of-file. It is not data in a stream. It is information that the stream is in an end-of-file state. For ordinary files, this means the read position is at the end of the file and there is no more data to read beyond that. For interactive streams, you can call `clearerr(stdin)` and attempt to read more. – Eric Postpischil Apr 23 '22 at 13:59
  • 4
    `getchar()` returns an `int`, not a `char`. You cannot compare a `char` with `EOF`. – mch Apr 23 '22 at 14:00
  • See [`while ((c = getc(file)) != EOF)` loop won't stop executing](https://stackoverflow.com/q/13694394/15168) for a discussion of the perils of assigning the return value of `getc()` or `getchar()` to a `char` instead of an `int`. – Jonathan Leffler Apr 23 '22 at 14:16
  • Once you've reached EOF, you've reached EOF — at least on sane Unix-like systems such as macOS. There was (maybe still is) an issue on Linux where EOF doesn't always mean EOF if the input is a terminal — IMO, it was/is a bug; it flies in contradiction of all other variants of Unix that I know of. If you want to 'read past EOF', you need to use `clearerr(stdin);` to clear the EOF and error bits in the stream. – Jonathan Leffler Apr 23 '22 at 14:21
  • Testing on Ubuntu 22.04 indicates that Linux/Glibc no longer has the "EOF is not really EOF" misfeature. I forget how long ago it was a problem (but I am sure it was this millennium and it was probably within the last decade); the chances are that you won't run into it. – Jonathan Leffler Apr 23 '22 at 14:28

1 Answers1

1

You must define c as an int to accommodate for the full range of possible return values from getchar(), namely all values of type unsigned char and the special negative value EOF (usually defined as (-1)).

On most unix systems, when Ctrl-D is typed in the terminal in canonical mode, whatever input has been buffered by the terminal is sent to the process. In your case, it causes the input to be echoed. If there is no such input pending, the terminal sends zero bytes to the process, which is interpreted by the OS as the end of file. Hitting Ctrl-D twice in a row, or more precisely at the beginning of a read request, does not enter an EOF byte, it signals the end of file to the reading process, hence any further attempt at reading from the stream will return EOF immediately without requesting more user input from the terminal.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • [Control-D does not actually signal EOF.](https://stackoverflow.com/a/21365313/298225) If you type “abc”, control-D, ”def”, no EOF will be indicated. – Eric Postpischil Apr 23 '22 at 14:23
  • @EricPostpischil: indeed, Ctrl-D must be typed at the beginning of a read request to signal end of file. – chqrlie Apr 23 '22 at 14:27
  • 1
    If you have typed any data on the line (before a newline), you have to type control-D twice consecutively; the first time to send whatever was already entered on the input line and the second to indicate 'zero characters available' which is what EOF means. – Jonathan Leffler Apr 23 '22 at 14:30
  • @JonathanLeffler: yes. I updated the answer with a more detailed explanation and a link to Eric's answer. – chqrlie Apr 23 '22 at 14:40
  • See also [Canonical vs non-canonical terminal input](https://stackoverflow.com/q/358342/15168). – Jonathan Leffler Apr 23 '22 at 14:44
  • 1
    @JonathanLeffler: I added a link to your great answer :) – chqrlie Apr 23 '22 at 14:48
  • I finally understand. I was thinking that EOF is similar to any other input ,for example '\n' just without any corresponding character in the character set, and it just functions as a terminating point only for that instance. Also, when you say echo are you referring to putchar(c)? – Siddhant Sood Apr 23 '22 at 14:56
  • @SiddhantSood: by *echo*, I mean the program outputs the characters it reads from the terminal. If you type **`abc`^D`def`Enter**, you first see **`abc`** as you type them (keys are echoed by the terminal) then you get another **`abc`** when you type **^D**, then **`def`** as you type them, then **`def`** again on a separate line after you hit **Enter**. – chqrlie Apr 23 '22 at 15:22