当通过管道提供 STDIN 时,getchar() 在 EOF 上循环

getchar() loops over EOF when STDIN provided through a pipe

我面对无法解释的事情。没有比举个例子更好的解释方法了:

#include <stdio.h>

int main ()
{
    char c;
    while (1) {
        c = getchar();
        printf("%x\n", c);
    }
   return(0);
}

如果我执行这个命令,它会这样无限循环:

$ echo -n "A" | ./binary
41
ffffffff
ffffffff
ffffffff
ffffffff
...

据我所知和阅读的内容 (Confusion about how a getchar() loop works internally),我认为 echo -n "A" 会发送一个 Astdin 然后触发一个 EOF event(我不确定 EOF 到底是什么)once,因此我的循环最多会迭代两次,然后会休眠等待 stdin.

中的新输入

但是不,它遍历 EOF,我不明白为什么。

我运行这个命令试着理解:

$ echo -n "A" | strace ./binary
read(0, "A", 4096)                      = 1
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 4), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7ff6000
write(1, "41\n", 341
)                     = 3
read(0, "", 4096)                       = 0
write(1, "ffffffff\n", 9ffffffff
)               = 9
read(0, "", 4096)                       = 0
write(1, "ffffffff\n", 9ffffffff
)               = 9
...

所以看起来 read() 没有读取任何内容,returns 0 被 getchar() 解释为 EOF。但是为什么,为什么它会像这样迭代,而当我以正常方式执行这个二进制文件时,它会按预期工作:

$ ./binary
A
41
a
B
42
a
^C
$

(上面的输出可能有点混乱,但是当我输入 A 然后 Return 时,我发送了 A 然后 \n (0x0a) 到 stdin,所以二进制只显示它们的十六进制表示,41a)

有人可以给我解释一下吗?我错过了什么?

非常感谢阅读!

一旦遇到EOFgetchar会立即return,return值EOF。流的流指针不会前进,它会停留在 EOF 标记上。对 getchar 的后续调用也将立即 return,因为流仍在 EOF 标记处。

请注意,这与 stdin 附加到输入设备时的行为不同。在这种情况下,getchar 将暂停,如果输入缓冲区为空,则等待进一步的输入。 getchar 不会 return EOF 直到 EOF 从输入设备发送(CTRL-D 从 [=26= 中的键盘发送 EOF ]).

getchar() return是一个 int,不是 char。较大的 return 值允许您查找 return 值 EOF,即 -1。这是错误和文件结尾的 return 值。将 c 的类型更改为 int 不会更改 printf 的行为。

#include <stdio.h>

int main ()
{
    int c;
    while (1) {
        c = getchar();
        if ( c == EOF) {
            break;
        }
        printf("%x\n", c);
    }
   return(0);
}

首先,EOF 为 -1,即 0xFFFFFFFF。

系统函数 'getchar()' returns 一个 int,而不是一个 char。

所以要进行比较,'c' 必须是 int,而不是 char。

现在,要退出 while 循环,必须与某些条件进行比较,该条件必须为真才能继续循环。

建议使用以下代码模型。

#include <stdio.h>

int main ( void )  //<< for main, better to use 'void' rather than empty braces
{
    int c; //<< because getchar() returns an int, not a char

    // following loop has EOF as exit condition
    // note in C, && is lower presidence than = and != so is evaluated last
    // note in C, && is evaluated from left to right
    // note to help the compiler to catch errors,
    //      always place the literal on the left of a comparison
    while (c = getchar() && EOF != c) 
    {
        printf("%x\n", c);
    }
   return(0);
} // end function: main