K&R书中的C编程练习

7
有没有想法为什么以下代码没有打印输入中的字符数? 我直接从K&R书中拿来的。目前正在学习C语言,这真的很困惑,看起来好像我永远也达不到EOF。如果是这样的话,那么为什么会将其用作示例呢?
#include <stdio.h>

main()
{
    double nc;

    for (nc = 0; getchar() != EOF; ++nc)
        ;
    printf("%d\n", nc);
}
6个回答

12
程序看起来正确,你的问题是在输入后必须向命令行发送EOF(文件结束符),因为标准输入被视为一个文件。我认为在Linux终端中,您可以按Ctrl+D键来发送EOF...我不确定在其他操作系统中该怎么做。为了测试这个程序,你可能想要把EOF改成'\n',这会使程序在你按下回车时停止。

1
对于你的参考,我没有给你投反对票。然而,实际上并不存在EOF字符,这可能是引起混淆的原因。当用户在终端上按下^D(或其他字符)时,它会导致进程的stdin出现EOF条件(由shell关闭),其中并没有任何字符参与。 - Chris Young
哦,我一直以为EOF是控制字符之一...谢谢你的信息。 - Paige Ruten
1
现在我意识到EOF字符没有意义,因为二进制文件通常会包含该字符码。 - Paige Ruten
1
CTRL-D实际上是EOT(传输结束)字符,当从终端发送时会导致EOF条件。 - tvanfosson
在一些早期的复古机器中,Ctrl Z 是一个 EOF 字符。 - EvilTeach

7
该程序将从 stdin 读取数据,直到接收到 EOF。在Unix控制台上,在一行的开头按下Ctrl-D;在Windows控制台上,在一行的开头按下Ctrl-Z。以这种方式发出信号后,将执行 printf 函数,显示读取的字符数。

6

您的nc是一个双精度浮点数—使用printf("%lf", nc)来打印双精度浮点数。

尝试使用下面这个

#include <stdio.h>

int main(void)
{
    int nc;

    for (nc = 0; getchar() != EOF; ++nc)
        ;
    printf("%d\n", nc);

    return 0;
}

虽然略有不妥,但%f是用于双精度浮点数的。由于变量参数函数的提升,它也可以用于浮点数。 - Chris Young
谢谢,但我会继续使用%lf,就像好书上说的那样 #include <stdio.h> int main(void) { long float x = 3.14159265358979; float y = 3.14159265358979f;printf("%15.10f\n", y); /* 3.1415927410 */ printf("%15.10lf\n", x); /* 3.1415926536 */ return 0;} - EvilTeach
如果你愿意,你可以坚持使用%lf,但这仍然是错误的。%f用于双精度浮点数。顺便说一下,ISO/IEC 9899:1999(俗称C99,不被许多实现支持)将%lf视为%f,但在ISO/IEC 9899:1990(俗称ANSI C或C89或C90)中,%lf是未定义的行为。 - Chris Young
另外,你的评论示例有误,不存在所谓的long float。对于浮点数和双精度浮点数,您使用%f,因为在传递给变量参数函数时,浮点数会被提升为双精度浮点数。 - Chris Young
@EvilTeach,请不要提倡使用%lf,这在C99中是合法的,以与scanf协调,但在之前的版本中是不合法的。由于无法将float传递给printf(或任何可变参数函数),因此使用%f表示double(无论是否从float提升)。 - Robert Gamble
显示剩余2条评论

5
我想澄清到目前为止给出的答案,因为它们似乎使用了诸如“发送EOF”,“接收EOF”,“EOF字符”等短语。根据此答案的评论(感谢),“发送EOF”和“接收EOF”是合法术语,但请不要认为它是一个字符。
EOF根本不是一个字符。如果流处于“文件结束”或读取错误发生,则getchar(或fgetc / getc)返回的值就是EOF。它只是一个特殊值,超出getchar()将返回的字符值范围,指示错误或文件结束的状态。
C标准将其定义为负数,而getchar将字符作为无符号字符转换为int返回。
编辑:在进行了一些研究之后,我意识到我之前写的段落中的一些假设完全是错误的。感谢评论者指出这一点。
一旦流(如stdin)处于文件结束状态,可以使用clearerr()清除此条件,并且getchar()可以从stdin读取更多数据。

哇,这里有很多需要纠正的地方:1)没有人称EOF为字符,2)“发送/接收EOF”是完全合法的短语,3)控制终端不会关闭您的文件,4)当从getchar返回EOF时,stdin并未关闭,您可以在接收EOF后继续从stdin读取... - Robert Gamble
  1. 实际上是Jeremy Ruten回答的,我纠正了他并且他编辑了他的回答;请查看修订历史记录。2) 好的。3) 我应该说的是shell。4) 当然,你可以继续从stdin读取,但你只会不断地得到EOF。如果你不同意,请提供一个例子。
- Chris Young
谢谢,但是你给那个示例程序什么输入?一旦我按下^D,第一个EOF和第二个EOF立即输出。也就是说,后续对getchar()的调用将返回EOF。 - Chris Young
如果我在第二个 while 循环之前放置 clearerr(stdin),那么我得到了我想要的行为。 - Chris Young
是的,有些系统需要先使用clearerr函数,我应该在那里加上这个。 - Robert Gamble
显示剩余4条评论

1

这段代码从标准输入读取字符。当您正常运行此程序时,标准输入来自用户。在这种情况下,没有办法向程序发送EOF。

如果您将文件重定向到标准输入(./myprogram < tempfile.txt),则此代码将起作用。


为什么在它当前的形式下被用作示例,当在它当前预期的形式下无法到达printf函数? - Joel Spolsky
你确定他们不想让你使用重定向吗?如果你按照示例完全输入,那么这本书看起来相当古老,也许以前可以工作,或者只适用于特定平台(可能已经不存在了,比如DOS)。 - SoapBox
特别是main()函数的定义,它没有指定返回类型,以及在for循环中使用double类型让我对这本书的年代和实用性产生了质疑。 - SoapBox
1
K&R的《C程序设计语言》是教授C语言的第一本书,它非常古老。 - Chris
main函数没有返回类型,因为它不返回任何东西。在C语言中是允许的。它在循环中使用了double类型,因为代码是为一个练习设计的,其中字符数超过了int类型的范围。该练习从文件中获取字符,用户不需要输入数百万个按键。 - Dour High Arch
显示剩余2条评论

0
首先,正如之前提到的,您需要使用%lf而不是%d来显示double,否则它将始终显示零(d是用于有符号整数)。然后,如果您想在基于Windows的操作系统上手动测试此功能,请键入一些字符,按Enter,然后单独按CtrlZ(这将模拟EOF)。打印的结果将比字符数多一个(它包括CtrlZ)。此外,正如SoapBox所指出的那样,在寻找EOF时,当时的想法通常是Unix,您可以通过标准输入将文件管道传输到程序中,这也可以工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接