0

test1.c

#include <stdio.h>

int main(void)
{
    int ch;
    FILE *fp;
    fp = fopen("file1.txt", "w");

    while ((ch = fgetc(stdin)) != EOF)
    {
        fputc(ch, fp);
    }

    printf("\n");

    return 0;
}

test2.c

#include <stdio.h>

int main(void)
{
    int ch;

    while ((ch = fgetc(stdin)) != EOF)
    {
        fputc(ch, stdout);
    }

    printf("\n");

    return 0;
}

The difference between the two files is the expression inside the while loop(one outputs to a file while another outputs to stdout).

The behavior of two program confuses me.

test1.c: I should type twice ctrl+D to complete my input. For example I type "123 ctrl+D ctrl+D", the first fgetc return and while loop run and finally ch can get EOF to run out of while loop.

test2.c: The fgetc return when I type ctrl+D just once. For example "123 ctrl+D", The fgetc return and while loop run but ch can not get EOF so it's blocked by the last fgetc.

Why?

My environment:

gcc (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
Linux ubuntu 4.15.0-76-generic #86-Ubuntu SMP x86_64 GNU/Linux
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Sheldon Hu
  • 23
  • 5
  • "The difference of test1.c and test2.c is that the expression inside the while." Could you fix that to explain the difference between the two files (and fix the code listing if it is not accurate - the code inside the while loops in both is identical.) – BadZen Feb 08 '20 at 04:36
  • fixed.. @BadZen – Sheldon Hu Feb 08 '20 at 04:51
  • `fgetc` returns an `int`, so `ch` should be an `int` to properly store EOF. – jamesdlin Feb 08 '20 at 04:52
  • 1
    @SheldonHu Well, `fgetc` returns EOF or the character value as an *unsigned* value. By storing its result in `char`, you can't *properly* store EOF because you won't be able to distinguish it from a legitimate value. – jamesdlin Feb 08 '20 at 05:02
  • These should behave exactly the same and I cannot reproduce your problem. They both exit immediately on a single ^D. – BadZen Feb 08 '20 at 05:02
  • 2
    See https://stackoverflow.com/questions/37959469/understanding-how-eof-and-ctrl-d-work and https://stackoverflow.com/questions/21260674/why-do-i-need-to-type-ctrl-d-twice-to-mark-end-of-file and https://stackoverflow.com/questions/358342/canonical-vs-non-canonical-terminal-input (probably among others). – jamesdlin Feb 08 '20 at 05:03
  • @jamesdlin Oh, right. Of course, terminal. – BadZen Feb 08 '20 at 05:05
  • `char` can store -127~128, so `char` can properly store EOF, it works in test1.c. Although it's nothing about `char` and `int`,I update the code. @jamesdlin – Sheldon Hu Feb 08 '20 at 05:05
  • I didn't mean to imply that misusing `char` for `fgetc` was the cause of your problem. I was pointing out a different problem (hence why I wrote it as a comment and not as an answer). – jamesdlin Feb 08 '20 at 05:07
  • I hope so too, unfortunately not .@BadZen – Sheldon Hu Feb 08 '20 at 05:07
  • See [`while ((c = getc(file)) != EOF)` loop won't stop executing](https://stackoverflow.com/a/13694450/15168) and [`int c = getchar()`?](https://stackoverflow.com/q/7119470/15168) for details on why you need to capture the result of `getchar()`, `getc()` or `fgetc()` in an `int` and not a `char`. Note that a plain `char` type can be signed or unsigned — though it is a distinct type from either `unsigned char` or `signed char`. Consequently, the range of `char` is not necessarily `-128 .. +127` (and is never the range `-127 .. +128`); it can also be `0 .. 255`. The range can be larger too. – Jonathan Leffler Feb 08 '20 at 05:32
  • Note that if the last character typed before Control-D was a newline, a single Control-D is sufficient to indicate EOF. If the last character was not a newline, then you need to type Control-D twice to indicate EOF. – Jonathan Leffler Feb 08 '20 at 05:34
  • `test1.c` behaves the same as you said,but `test2.c` not. Have you run `test2.c` ? @JonathanLeffler – Sheldon Hu Feb 08 '20 at 05:46
  • I find that hard to believe. I'm not at my machine with an Ubuntu VM. However, on my MacBook Pro running macOS Mojave 10.14.6, the two programs both behave as I stated. There was a time when the C library on Linux didn't behave 'properly' w.r.t EOF, but the context was trying to read after getting EOF, not this 'double Control-D' behaviour. Is it Ubuntu 18.04 LTS that you're using? – Jonathan Leffler Feb 08 '20 at 05:52
  • I switched to my MacBook Pro running an Ubuntu 18.04 LTS VM under VMWare Fusion (`uname -a` reports `Linux ubuntu 4.15.0-74-generic #84-Ubuntu SMP Thu Dec 19 08:06:28 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux`) and running `test2`, I have to type control-D twice to get it to stop (before a newline; once is sufficient immediately after a newline). – Jonathan Leffler Feb 08 '20 at 06:00
  • OK — I've run the software updater and now have `Linux ubuntu 4.15.0-76-generic #86-Ubuntu SMP Fri Jan 17 17:24:28 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux` which is what you're running — and I still get `test2` requiring two Control-D's mid-line, only one after a newline. Obviously, this _is_ in a VM, but that shouldn't be a factor in the problem. – Jonathan Leffler Feb 08 '20 at 06:20
  • emmm... Sorry! It's my fault. Thanks for your patience. @JonathanLeffler – Sheldon Hu Feb 08 '20 at 06:34
  • NP: I hadn't realized my VM wasn't up to date. – Jonathan Leffler Feb 08 '20 at 06:39
  • ‍♂️‍♂️‍♂️ – Sheldon Hu Feb 08 '20 at 06:44

0 Answers0