7

它似乎在按下键scanf时开始扫描输入,我想用下面的代码来验证这一点(为简单起见,我消除了错误检查和处理)。Enter

#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,两者的结果相同。

问题是:

  1. 如何解释启用和禁用输入缓冲时的不同行为?
  2. 回到最初的问题,什么时候scanf开始扫描用户输入?输入字符的那一刻?还是缓冲直到一行完成?
4

1 回答 1

11

有趣的问题——冗长的答案。如有疑问,我正在描述我认为在 Unix 上发生的事情;我把 Windows 留给其他人。我认为行为会相似,但我不确定。

使用 时,您使用系统调用setvbuf(stdin, NULL, _IONBF, 0)强制stdin流一次读取一个字符。read(0, buffer, 1)当您使用_IOFBFor运行时_IOLBF,管理流的代码将尝试一次读取更多字节(如果您使用setvbuf()BUFSIZ不使用,则最多可达您提供的缓冲区的大小)。这些观察结果加上输入中的空格是解释发生了什么的关键。我假设您的终端处于正常或规范输入模式 - 请参阅规范与非规范终端输入以进行讨论。

您是正确的,在您键入 return 之前,终端驱动程序不会使任何字符可用。这允许您在键入时使用退格键等来编辑该行。

当您按下回车键时,内核有 4 个字符可用于发送给任何想要读取它们的程序:1 2 space return.

在您使用的情况下,这 4 个字符会通过诸如 的调用_IONBF一次性读入标准 I/O 缓冲区。然后从缓冲区中收集、 the和字符,并将空间放回缓冲区中。(请注意,内核已将所有四个字符都传递给程序。)程序打印其输出并退出。shell 恢复,打印一个提示并等待更多输入可用 - 但在用户键入另一个 之前,不会有任何输入可用,可能(通常)前面有一些其他字符。stdinread(0, buffer, BUFSIZ)scanf()12spacereturn

在您使用 的情况下程序_IONBF一次读取一个字符。它read()调用获取一个字符并获取1; 它再次read()调用并获得2; 它再次read()调用并获取space角色。(请注意,内核仍然有return准备好等待。)它不需要空间来解释数字,所以它把它放回它的推回缓冲区(推回缓冲区中保证至少有一个字节的空间),为下一个标准做好准备 I /O 读操作,并返回。程序打印其输出并退出。shell 恢复,打印提示,并尝试从终端读取新命令。内核通过返回正在等待的换行符来强制执行,shell 会说“哦,这是一个空命令”并给你另一个提示。

1 2 x p s return您可以通过键入( _IONBF) 程序来证明这就是发生的情况。当你这样做时,你的程序读取值 12 和“x”,留下“ps”和换行符由 shell 读取,然后 shell 将执行ps命令(不回显它读取的字符),然后提示再次。

您还可以使用trussorstrace或类似的命令来跟踪程序执行的系统调用,以查看我建议发生的事情的真实性。

于 2012-10-17T03:59:12.903 回答