2
votes

This kind of questions might seem as one of those geeky considerations, but I rather ask you for thorough explanation of what is going on to satisfy my curiosity.

Let's assume we have a following chunk of code:

#include <stdio.h>

int main(void)
{
    char ch;

    scanf("%c", &ch);
    printf("%d\n", ch);

    return 0;
}

After compilation, one can type at the beginning of line simulated EOF with CTRL+Z shortcut and press ENTER - this is done twice.

Output looks like this:
^Z
^Z
-52
Press any key to continue . . .

1) What is happening right now?

I have another doubt concerning such loop:

while (scanf("%c", &ch) != EOF)
    printf("%c\n", ch);

printf("BYE!\n");

Output will be:
^Z
^Z
BYE!
Press any key to continue . . .

2) Why is not it terminating after first EOF simulation?

EDIT: I searched for another answers on SO relating to my doubts and I think that it is difficult to use scanf(), as it has got better substitutes like fgets() or fread(). Please take a look on another example below:

int input;
char ch;

while (scanf("%d", &input) != 1) //checking for bad input
    while ((ch = getchar()) != '\n') //disposing of bad input
        putchar(ch);

printf("BYE!\n");

I input five times CTRL+Z at the beginning of line and the output would become:
^Z
^Z
^Z
^Z
 ^Z
 ^Z
 ^Z
 ^Z
 ^Z
 ^Z
 ^CPress any key to continue . . .
I added five more EOFs and had to kill program with CTRL+C on the last line.

3) Why the space appeared on the 5-th line and was visible to the end (sometimes two spaces are before '^CPress any key to continue . . .')?

Last example is amendment of loop from above (there is no meaning in code):

while (scanf("%d", &input) != EOF);  

printf("BYE!\n");  

Output is:
^Z
^Z
^Z
BYE!
Press any key to continue . . .

4) Why are we using three times CTRL+Z instead of two as it was written in above comment?

1
(1) When you indicate EOF with Control-Z, nothing is assigned to ch so you get an indeterminate (semi-random) value printed for ch. (2) On Unix, if you indicated EOF (with Control-D, usually) as the first character of input, then you'd not need to repeat it; if you've typed a character, say a blank, and then type Control-D, that sends the blank to the program, but the programs continues to wait for a newline — or another EOF. If you're on Windows (plausible since you're using Control-Z rather than Control-D), the rules may be slightly different; you might have to indicate EOF twice always. - Jonathan Leffler
Step 1: Do not use ch when scanf("%c", &ch) != 1 as in scanf("%c", &ch); printf("%d\n", ch); - chux - Reinstate Monica
@JonathanLeffler Thank You! I am using Windows right now and it might use double EOF simulation. What about if buffer (suppose we use line-buffering) has following chars: abc'EOF'\n? Why does it print graphical representation of control character and move carriage one position to the right instead of finishing one char before? - MrDonMatti
If you have type abc and then Control-Z once, the characters abc are made available to functions such as getchar(). Since getchar() doesn't care about newlines, the code should print a and then the loop-less code will exit. If you loop, the code will read the three characters and then wait for more input. If you type another Control-Z, the underlying read() system call returns 0 bytes available, which is the indication of EOF. If you use a line-based input (fgets() or POSIX getline()), those won't return the abc until either a newline or the EOF is read. - Jonathan Leffler
There are definitely other questions here on SO about EOF and typing Control-D or Control-Z to the program, and so on. One such is heavily Unix oriented, but may still help on Windows: Canonical vs non-canonical terminal input. There are almost certainly others more immediately relevant to you, but I missed them in my 30-minute scan (but I was doing some other clean-up etc while scanning, and the scan was using a not very focussed search). - Jonathan Leffler

1 Answers

0
votes

Your code invokes undefined behavior. If scanf() cannot read a byte for format string "%c", it returns EOF and leaves ch uninitialized.

It is better to write the code this way:

while (scanf("%c", &ch) == 1)
    printf("%c\n", ch);

printf("BYE!\n");