我想了解getchar()!= EOF

Pao*_*oni 33 c putchar eof getchar

我正在阅读C语言程序,并且到目前为止已经理解了所有内容.然而,当我遇到getchar()并且putchar(),我无法理解它们的用途,更具体地说,以下代码的作用.

main()
{
    int c;
    while ((c = getchar()) != EOF)
       putchar(c);
}
Run Code Online (Sandbox Code Playgroud)

我理解main()函数,整数的声明cwhile循环.然而我对while循环中的条件感到困惑.这个C代码的输入是什么,输出是什么.

对不起,如果这是一个基本而愚蠢的问题,但我只是在寻找一个简单的解释,然后再继续阅读本书并变得更加困惑.

Oli*_*rth 30

此代码可以更清晰地编写为:

main()
{
    int c;
    while (1) {
        c = getchar();            // Get one character from the input
        if (c == EOF) { break; }  // Exit the loop if we receive EOF ("end of file")
        putchar(c);               // Put the character to the output
    }
}
Run Code Online (Sandbox Code Playgroud)

EOF当没有更多的输入接收的字符.在从真实文件读取输入而不是用户输入(这是文件的特殊情况)的情况下,名称更有意义.


[顺便说一句,一般来说,main函数应该写成int main(void).]

  • `EOF` 不是一个字符。 (2认同)

Pab*_*ruz 18

getchar()是一个从标准输入中读取字符的函数.EOFC中用于表示已达到END OF FILE的特殊字符.

通常EOF,getchar()当标准输入不是控制台(即文件)时,您将获得一个返回的字符.

如果您在unix中运行您的程序,如下所示:

$ cat somefile | ./your_program
Run Code Online (Sandbox Code Playgroud)

然后你的getchar()意志将返回每一个角色,somefileEOF尽快somefile结束.

如果您运行这样的程序:

$ ./your_program
Run Code Online (Sandbox Code Playgroud)

EOF通过控制台发送(通过CTRL+D在Windows中按Unix或CTRL + Z),然后getchar()也将返回EOF并执行将结束.


Ant*_*ala 7

用当前的 C 标准编写的代码应该是

#include <stdio.h>

int main(void)
{
    int c;
    while ((c = getchar()) != EOF)
       putchar(c);
}
Run Code Online (Sandbox Code Playgroud)

循环可以改写为

int c;
while (1) {
    c = getchar();
    if (c != EOF)
        putchar(c);
    else
        break;
}
Run Code Online (Sandbox Code Playgroud)

这读作

  • 永远重复
    • 标准输入获取输入的下一个字符(“字节”)并将其存储到c
    • 如果在读取所述字符时没有发生异常情况
      • 然后将存储的字符输出c标准输出中
    • 别的
      • 打破循环

许多编程语言通过引发破坏正常程序流程的异常来处理异常情况。C 没有这样的事情。相反,可能失败的函数有一个返回值,任何异常情况都由一个特殊的返回值表示,您需要从给定函数的文档中检查。对于getchar,来自 C11 标准的文档说(C11 7.21.7.6p3):

  1. getchar函数从 指向的输入流中返回下一个字符stdin。如果流位于文件尾,则设置流的文件尾指示符并getchar返回EOF。如果发生读取错误,则设置流的错误指示符并getchar返回EOF

在别处说明这EOF是一个 < 0 的整数常量,并且任何普通的返回值是 >= 0 -unsigned char零扩展到int.

位于文件末尾的流意味着所有输入都已被消耗。对于标准输入,可以通过打字键盘从导致这种Ctrl+D在Unix / Linux终端和Ctrl+Z在Windows控制台窗口。另一种可能性是程序从文件或管道而不是从键盘接收输入 - 然后每当输入被完全消耗时就会发出文件结束信号,即

cat file | ./myprogram
Run Code Online (Sandbox Code Playgroud)

或者

./myprogram < file
Run Code Online (Sandbox Code Playgroud)

正如上面的片段所说,实际上有两种不同的情况会导致getchar返回EOF:到达文件末尾或者发生了实际错误。这不能仅从返回值中推导出来。相反,您必须使用函数feofferrorfeof(stdin)如果在标准输入上到达文件结尾,将返回一个真值。ferror(stdin)如果发生错误,将返回 true。

如果发生实际错误,则errno定义的变量<errno.h>将包含错误代码;该函数perror可用于自动显示带有前缀的人类可读错误消息。因此我们可以将示例扩展为

#include <stdio.h>
#include <errno.h> // for the definition of errno
#include <stdlib.h> // for exit()
int main(void)
{
    int c;
    while ((c = getchar()) != EOF)
       putchar(c);

    if (feof(stdin)) {
        printf("end-of-file reached\n");
        exit(0);
    }
    else if (ferror(stdin)) {
        printf("An error occurred. errno set to %d\n", errno);
        perror("Human readable explanation");
        exit(1);
    }
    else {
        printf("This should never happen...\n");
        exit('?');
    }
}
Run Code Online (Sandbox Code Playgroud)

要触发文件结尾,可以^D在 Linux 上的新行上使用 Ctrl+D(此处显示为):

% ./a.out
Hello world
Hello world
^D
end-of-file reached
Run Code Online (Sandbox Code Playgroud)

(注意这里的输入是如何行缓冲的,所以输入不会在输出行内交错)。

同样,我们可以通过使用管道来获得相同的效果。

% echo Hello world | ./a.out
Hello world
end-of-file reached
Run Code Online (Sandbox Code Playgroud)

触发错误有点棘手。在bashzshshell 中,可以通过附加到命令行来关闭标准输入,使其不会来自任何地方<&-

% ./a.out <&-
An error occurred. errno set to 9
Human readable explanation: Bad file descriptor
Run Code Online (Sandbox Code Playgroud)

Bad file descriptor,或者EBADF意味着标准输入- 文件描述符编号 0 无效,因为它根本没有打开。

另一种产生错误的有趣方式是从目录中读取标准输入——这会导致EISDIR在 Linux 上将errno 设置为:

% ./a.out < / 
An error occurred. errno set to 21
Human readable explanation: Is a directory
Run Code Online (Sandbox Code Playgroud)

实际上也putchar应该检查的返回值- 它同样返回EOF错误或写入的字符:

while ((c = getchar()) != EOF) {
    if (putchar(c) == EOF) {
        perror("putchar failed");
        exit(1);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以通过将标准输出重定向/dev/full到来测试这一点——但是有一个问题——因为标准输出是缓冲的,我们需要写足够的内容来使缓冲区立即刷新,而不是在程序结束时刷新。我们从/dev/zero以下获得无限零字节:

 % ./a.out < /dev/zero > /dev/full
 putchar failed: No space left on device
Run Code Online (Sandbox Code Playgroud)

PS 始终使用类型变量int来存储getchar(). 尽管它读取一个字符使用signed/ unsigned/纯char永远是错的


J..*_*..S 6

getchar() 函数从键盘读取一个字符(即stdin

\n\n

在给定循环内的条件中whilegetchar()在每次迭代之前调用,并将接收到的值分配给整数c

\n\n

现在,必须明白,在 C 中,标准输入 ( stdin)就像一个文件。即,输入被缓冲。输入将保留在缓冲区中直到被实际消耗。\nstdin实际上是标准输入流

\n\n

getchar()返回输入缓冲区中的下一个可用值。

\n\n

该程序本质上显示从键盘读取的任何内容;包括空白,如\\n(换行符)、空格等。

\n\n

即,输入是用户通过键盘(stdin通常指键盘)提供的输入。\n输出是我们提供的输入。

\n\n

我们提供的输入会被逐个字符地读取并被视为字符,即使我们将它们作为数字给出。

\n\n

getchar()EOF仅当到达文件末尾时才会返回。我们这里关心的\xe2\x80\x98file\xe2\x80\x99就是它本身stdin(标准输入)。

\n\n

想象一下存在一个文件,其中存储了我们通过键盘提供的输入。那\xe2\x80\x99s stdin。\n这个\xe2\x80\x98file\xe2\x80\x99 就像一个无限文件。所以不EOF。\n

\n\n

如果我们提供的输入多于getchar()一次可以处理的输入(在按 Enter 键将其作为输入之前),多余的值仍将存储在输入缓冲区中未使用。\n将从输入中读取 getchar()第一个字符,将其存储在c and printc withputchar(c)`.

\n\n

在循环的下一次迭代期间,在上一次迭代期间给出的仍在该部分while中的额外字符将在该部分中stdin被获取 。\n现在重复相同的过程,直到输入缓冲区中没有任何内容为止。while ((c = getchar()) != EOF)c=getchar()

\n\n

putchar()如果在迭代期间给出多个字符作为输入,这使得它看起来好像一次返回一个字符串而不是单个字符。

\n\n

例如:如果输入是
\n abcdefghijkl
\n输出将\xe2\x80\x99相同
\nabcdefghijkl

\n\n

如果你不\xe2\x80\x99不想要这种行为,你可以添加fflush(stdin); 紧接在putchar(c);.\n之后,这将导致循环仅打印每次迭代期间提供的输入中的第一个字符。

\n\n

例如:如果输入是
\n adgbad
\nonlya将被打印。

\n\n

stdin仅当您按 Enter 键后,输入才会发送到。

\n\n

putchar()与 .putchar() 相反getchar()。它将输出写入标准输出流(stdout通常是监视器)。

\n\n

\nEOF不是文件中存在的字符。它\xe2\x80\x99是函数返回的错误代码。

\n\n

不过,您可能无法while正常退出 \xe2\x80\x99 循环。一旦有东西通过键盘进入输入缓冲区,输入缓冲区就会清空(用于​​显示到输出),并且不会stdin给出EOF.

\n\n

对于手动退出循环,EOF可以使用键盘发送,在 Linux 中按 \n ctrl+和\n +D
ctrlZ ,在 Windows 中

\n\n

例如:

\n\n
while ((c = getchar()) != EOF)\n{\n\n   putchar(c);\n   fflush(stdin);\n}\nprintf("\\nGot past!");\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果您按组合键给出EOF,则消息Got past!,则在退出程序之前将显示

\n\n

如果stdin尚未空,则必须按此组合键两次。一次清除该缓冲区,然后模拟EOF

\n\n

c = getchar()编辑: in周围的额外一对括号while ((c = getchar()) != EOF)是为了确保返回的值在与值进行比较之前getchar()首先被分配给cEOF

\n\n

如果没有这个额外的括号,则表达式实际上将意味着while (c = (getchar() != EOF) )可以c有两个值之一:(1对于 true)或0(对于 false),这显然不是预期的。

\n