当按下 Enter 键时,似乎scanf
开始扫描输入,我想用下面的代码验证这一点(为了简单起见,我省略了错误检查和处理)。 / p>
#include <stdio.h>
int main(int argc, char **argv) {
/* disable buffering */
setvbuf(stdin, NULL, _IONBF, 0);
int number;
scanf("%d", &number);
printf("number: %d\n", number);
return 0;
}
这是另一个问题,在我禁用输入缓冲之后(只是为了验证结果;我知道我应该接下来 - 实际上从不这样做,以防它干扰结果),输出是(注意额外的提示) :
$ ./ionbf
12(space)(enter)
number: 12
$
$
与启用输入缓冲时的输出不同(无额外提示):
$ ./iofbf
12(space)(enter)
number: 12
$
启用缓冲区时似乎消耗了新行字符。我在两台不同的机器上进行了测试,一台安装了gcc 4.1.2和bash 3.2.25,另一台安装了gcc 4.4.4和bash 4.1.5,结果两者都相同。
问题是:
scanf
什么时候开始扫描用户输入?角色进入的那一刻?或者它是否缓冲直到一行完成?答案 0 :(得分:11)
有趣的问题 - 冗长的回答。如有疑问,我正在描述我认为在Unix上发生的事情;我将Windows留给其他人。我认为行为类似,但我不确定。
使用setvbuf(stdin, NULL, _IONBF, 0)
时,强制stdin
流使用read(0, buffer, 1)
系统调用一次读取一个字符。当您使用_IOFBF
或_IOLBF
运行时,管理流的代码将尝试一次读取更多字节(如果使用setvbuf()
,则最多可达到您提供的缓冲区大小,如果你不这样做,BUFSIZ
。这些观察结果加上输入中的空间是解释所发生情况的关键。我假设您的终端处于正常或规范输入模式 - 请参阅Canonical vs non-canonical terminal input进行讨论。
在输入return之前,终端驱动程序没有任何字符可用是正确的。这允许您在键入时使用退格等编辑行。
当你点击返回时,内核有4个字符可供发送给任何想要读取它们的程序: 1 2 space < KBD>返回
如果您不使用_IONBF
,那么这4个字符都会被stdin
的标准I / O缓冲区一次性读取。 read(0, buffer, BUFSIZ)
。然后scanf()
从缓冲区收集 1 , 2 和 space 字符,并将空格放回缓冲区。 (请注意,内核已将所有四个字符传递给程序。)程序打印其输出并退出。 shell恢复,打印提示并等待更多输入可用 - 但是在用户键入另一个 return 之前不会有任何可用输入,可能(通常)前面有一些其他字符。
如果 使用_IONBF
,程序会一次读取一个字符。它进行read()
调用以获取一个字符并获取 1 ;它进行另一次read()
调用并得到 2 ;它进行另一个read()
调用并获取 space 字符。 (注意,内核仍然有 return 准备好并等待。)它不需要空间来解释数字,所以它将它放回到它的回推缓冲区中(保证有空间用于至少一个字节在回推缓冲区中),准备好进行下一次标准I / O读操作,并返回。程序打印输出并退出。 shell恢复,打印提示,并尝试从终端读取新命令。内核要求返回正在等待的换行符,shell会说“哦,这是一个空命令”并给你另一个提示。
您可以通过键入 1 2 x p s来证明这是发生的事情 返回到你的(_IONBF
)程序。当你这样做时,你的程序读取值12和'x',留下'ps'和shell读取换行符,然后执行ps
命令(不回显它读取的字符) ),然后再次提示。
您还可以使用truss
或strace
或类似的命令来跟踪程序执行的系统调用,以查看我建议发生的真实性。