getchar()和putchar()函数的原理

10

我正在学习 K&R 的《C程序设计语言》,但是例子1.5让我困惑了:

#include <stdio.h>

/* copy input to output; 1st version */
int main(int argc, char *argv[])
{
    int c;

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

    return 0;
}

我理解'getchar()'接受字符以供'putchar()'显示。但是,当我在终端中运行程序时,为什么我可以传递一整行字符以供'putchar()'显示?

很简单,应该是重复的,不管怎样看答案 :) - 0decimal0
3个回答

24
因为你的终端是行缓冲的。 getchar()putchar() 仍然只能处理单个字符,但终端会等待直到你输入了整行字符才将它们提交给程序。然后,getchar() 逐个从该缓冲区中获取字符,putchar() 逐个显示它们。 补充:终端是行缓冲的意思是遇到换行符时才向程序提交输入。通常情况下,一次提交块数据比逐个字符提交更有效率。同时,它也为用户提供了在按下回车键之前编辑该行的机会。 注意:可以通过禁用终端的规范模式并在stdin上调用setbuf函数并传入NULL来关闭行缓冲。

+1 for the explanation. 但您能否更详细地解释一下这句话的含义:“因为您的终端是行缓冲的”? - Vivek Sadh
“通过禁用终端的规范模式,可以关闭行缓冲。但这还不够。您还需要通过setbuf/setvbuf关闭stdin的行缓冲。” - Jim Balter
实际上,我认为我可能错了,至少对于Linux来说是这样。我目前没有Linux系统进行测试,但我现在的理解是,当缓冲区为空时,getchar会读取缓冲区,如果该读取提前终止,则不会尝试继续读取以查找换行符,它会立即返回缓冲区的第一个字符,因此禁用规范模式是足够的。 - Jim Balter

2
“是的,只要不是 EOF 字符,你实际上可以写任何你想写的东西。键盘是一种特殊的 I/O 设备,它直接通过 BIOS 运行,并且在键盘上输入的字符会直接插入到缓冲区中。在你的情况下,这个缓冲区被 getchar() 函数读取。当你输入一个句子时,你正在向缓冲区推送数据,而 getchar() 函数则处于一个无限循环中。这就是为什么它能够工作的原因。”
“如果你想了解更多关于 I/O 设备工作方式的细节,可以再问我。”
“祝好!”

@MatteoItalia 是的,有...在POSIX上是ctrl-D,在Windows上是ctrl-Z。然而,这个答案的大部分都是错误的...一个人不是从产生键码的键盘读取,而是从通常与窗口相关联的虚拟设备终端读取;除非从控制台读取,否则BIOS与此无关...而且BIOS肯定不会缓冲这些数据。“如果您想了解有关IO设备工作方式的更多详细信息,请随时问我更多问题。”——一想到这个我就不寒而栗。 - Jim Balter
@JimBalter: 它们只是终端仿真器识别的魔术序列(仅当它们放在自己的一行中时); 终端仿真器看到该序列并关闭其管道的末端。 另一方面,C运行时看到流已关闭 - read 返回零,ReadFile 做类似的操作,并且可以检查文件结尾的流,没有 EOF 字符参与,并标记其自己流的结尾。 当应用程序到达 C 流的结尾时,CRT将EOF作为错误代码返回。 - Matteo Italia
1
我的观点是EOF不是一个真正的字符,而是一个错误代码,Ctrl-D/Ctrl-Z只是键盘快捷键。事实上,如果你将Ctrl-D存储在文件中并对其进行cat(甚至将其管道传输到另一个cat中,以确保在读取文件时它不会有不同的作用),它不会在Ctrl-D处停止,而是在“真正”的文件结尾处停止,因此这个字符本身并不神奇,而是流的关闭起了作用。否则,Ctrl-W就是“关闭窗口字符”,Ctrl-S就是“保存文件字符”,等等。 - Matteo Italia
@MatteoItalia 你的观点是错误的。Ctrl-D和Ctrl-Z是真正的字符...它们在各自的系统上被指定为“EOF字符”。 “如果你存储Ctrl-D”...当然,在POSIX系统上,EOF字符仅适用于终端。但在Windows文本文件中,ctrl-Z仍然是EOF字符。“所以不像是字符本身是神奇的”——“字符本身”只是一系列位序列;“魔法”在于它如何被解释否则,Ctrl-W将成为“关闭窗口字符”有些人称之为这样,他们也没有错。哦,而EOF不是错误代码。 - Jim Balter
奇怪的是你认为“CP/M时代”有什么不同。如果你说没有“EOF字符”,那么你也可以说没有“A字符”,只有一个以A显示的字节值。但这对于语言及其使用方式来说是非常混乱的。在POSIX中,“EOF字符”是指向终端驱动程序发出文件结束条件的字符,默认情况下为EOT(ctrl-D)。 - Jim Balter
显示剩余2条评论

0
考虑以下程序,
该程序获取输入(运行getchar)直到我们输入“$”字符并打印输出。
#include <stdio.h>
int main()
{
int c;
while ((c = getchar()) != '$') 
    putchar(c);
return 0;
}

如果我们输入abcd$$$$$$ [Enter],它会将输入存储在缓冲区中直到最后一个$符号。按下Enter键后,程序(while循环)开始运行,并且getchar函数逐个接收并打印字符,从'a'到第一个'$'。 只有在输入中按下'$',程序才会结束。

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