【问题标题】:File copying with getchar() and putchar()使用 getchar() 和 putchar() 进行文件复制
【发布时间】:2018-09-10 23:53:30
【问题描述】:

我知道这个问题之前已经讨论过,但我想确保我理解正确,这个程序发生了什么,以及为什么。在 Dennis Ritchie 的教科书 The C Programming Language 的第 20 页上,我们看到了这个程序:

#include <stdio.h>

int main()
{

int c;

c = getchar();

while(c != EOF){
    putchar(c);
    c = getchar();
}

return 0;

}

执行时,程序会读取输入的每个字符,并在用户按回车后以相同的顺序将它们打印出来。除非用户手动退出控制台,否则此过程会无限重复。事件顺序如下:

  1. getchar() 函数读取键入的第一个字符并将其值分配给c

  2. 因为c是整数类型,所以getchar()传递给c的字符值被提升为它对应的ASCII整数值。

  3. 现在c 已被初始化为某个整数值,while 循环可以测试该值是否等于文件结尾字符。因为EOF 字符具有-1 的宏值,并且因为可以键入的所有字符都没有负十进制ASCII 值,所以while 循环的条件将始终为真。

  4. 一旦程序验证c != EOF为真,就会调用putchar()函数,输出c中包含的字符值。

  5. 再次调用getchar(),因此它读取下一个输入字符并将其值传回while循环的开头。如果用户在执行前只键入一个字符,则程序读取&lt;return&gt; 值作为下一个字符并打印一个新行并等待键入下一个输入。

这些远程正确吗?

【问题讨论】:

  • 没有“EOF 字符”这样的东西。正如您所指出的,EOF 是整数值 -1,它不同于任何可能的字符值。 getchar() 返回一个 int,它可以是一个字符或 EOF,它会终止程序。如何在控制台上导致“EOF”条件取决于操作系统。当然,如果您的输入是从文件重定向的,那么它会以实际的 EOF 条件自然终止。
  • 这就是为什么 c 必须声明为整数而不是字符的原因吗?这样表达式 c != EOF 就可以计算了吗?
  • after the user hits enter 的想法与标准输入缓冲有关。它是行缓冲的,用户输入的内容由操作系统存储在缓冲区中,只有在用户输入(或 EOF)后才刷新到程序中。
  • 1.是,2.不,3.不,4.是,5.是
  • 请注意,在终端,您可以键入一个字符,该字符最终会被解释为表示 EOF。在 Unix 上,通常是 Control-D;在 Windows 上,Control-Z。但是,该字符不是 EOF;它可以出现在文件中并且(至少在 Unix 上)它只是另一个有效字符。在终端上键入时,终端驱动程序使任何等待的输入可用于从终端读取的程序。如果没有数据等待,则表明有 0 个字节可用,这就是触发 getchar() 等将其视为 EOF 的原因。当 read 返回 0 时,您在常规文件中到达 EOF。

标签: c eof getchar putchar


【解决方案1】:

是的,你基本上明白了。但它更简单:getcharputchar 已经分别返回和接受 int 类型。所以没有发生类型提升。你只是接收字符并循环发送它们直到你看到EOF

您对为什么这些应该是 int 而不是某些 char 形式的直觉可能是正确的:int 类型允许在任何可能的字符值的值范围之外的标记 EOF 值。

(K&R stdio 函数在这一点上非常老了,他们不知道 Unicode 等,而且一些基本的设计原理如果不是模糊的话,只是不相关。现在没有多少实用代码会用到这些函数。这本书在很多方面都很出色,但代码示例相当陈旧。)

(另外,fwiw,您的问题标题是指“复制文件”,您仍然可以这样做,但还有更多规范的方法)

【讨论】:

  • 有没有办法在读取所有用户输入后查看打印的 EOF 值,或者在您完成输入后输出 -1 的某种函数?
  • @NicholasCousar:我不太理解——你的意思是什么? getchar 在完成向您发送输入后,确实应该吐出 EOF (-1)。它理解“输入完成”的方式取决于它如何获得输入。如果您从命令行输入它,shell 将为您指示终止。如果您让它坐在那里接受键盘输入,您必须手动告诉它您已完成输入,请参阅此问题和答案(包括上面的 cmets),这可能会有所帮助stackoverflow.com/questions/21364313/…
  • EOF 不是一件事,而是一个事件。 getchar() 通过返回 -1 来通知您该事件。
【解决方案2】:

嗯,想法是正确的,但细节上不正确,这就是魔鬼所在。

  • getchar() 函数从标准输入中读取第一个字符并将其作为提升为 intunsigned char (如果没有读取到字符,则返回特殊的 EOF 值)

  • 返回值被赋值给c,它的类型是int(应该是if it were a char strange things could happen

  • 现在c 已分配了一个整数值,while 循环可以测试该值是否等于EOF 宏的值。

  • 因为EOF宏有一个实现指定的负值,并且因为字符被转换为unsigned char并提升为int,所以它们都没有负值值(至少在您遇到新手的任何系统中都不会),while 循环的条件将始终为真,直到文件结束条件发生读取标准输入。

  • 一旦程序验证c != EOF为真,就会调用putchar()函数,输出c中包含的字符值。

  • getchar() 再次被调用,因此它读取下一个输入字符并将其值传递回 while 循环的开头。

  • 标准输入,如果它连接到终端设备,通常是行缓冲的,这意味着程序在用户完成该行并点击 Enter 键。

我们说的是 执行字符集,而不是 ASCII,它现在可能通常是 UTF-8 编码的 Unicode 字符的单个字节。 EOF 在二进制中也是负数,我们不需要考虑“它的十进制值”。 charunsigned char 类型也是数字,字符常量的类型为 int - 即在执行字符集与 ASCII 兼容的系统上,编写 ' ' 将是和写 32 一样,当然对于那些不记得 ASCII 码的人来说更清楚。

最后,C 对初始化的含义非常严格。就是在声明变量的时候把初始值设置成变量。

int c = getchar();

有一个初始化。

int c;
c = getchar();

c 未初始化,然后分配了一个值。当编译器错误消息引用 initializationassignment 时,了解它们的区别可以更容易理解它们。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-17
    • 2013-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多