scanf何时开始和停止扫描?

时间:2012-10-17 02:34:38

标签: c io buffer scanf

当按下 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,结果两者都相同。

问题是:

  1. 如何解释启用和禁用输入缓冲时的不同行为?
  2. 回到原来的问题,scanf什么时候开始扫描用户输入?角色进入的那一刻?或者它是否缓冲直到一行完成?

1 个答案:

答案 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命令(不回显它读取的字符) ),然后再次提示。

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