【问题标题】:When does scanf start and stop scanning?scanf 什么时候开始和停止扫描?
【发布时间】:2012-10-17 02:34:38
【问题描述】:

似乎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什么时候开始扫描用户输入?输入字符的那一刻?还是一直缓冲到一行完成?

【问题讨论】:

  • 什么额外提示?您的代码没有打印出来!这是典型的用户错误
  • @AdrianCornish 如果用户出错,我应该如何更正?
  • @AdrianCornish- 我不知道您的评论是否正确,但“更改用户!”评论是史诗:-D
  • @Adrian 你错了,但你的评论也错了,但很有吸引力:D +1
  • 显示打印 $'s 的代码行

标签: c io buffer scanf


【解决方案1】:

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

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

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

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

如果您使用_IONBF,则通过read(0, buffer, BUFSIZ) 等调用将这4 个字符全部读入stdin 的标准I/O 缓冲区。 scanf() 然后从缓冲区收集 12space 字符,并将空间放回缓冲区。 (请注意,内核已将所有四个字符都传递给程序。)程序打印其输出并退出。 shell 继续,打印一个提示并等待更多输入可用——但在用户键入另一个 return 之前不会有任何输入可用,可能(通常)前面有一些其他字符。

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

你可以通过输入 1 2 x p s kbd> 返回到您的 (_IONBF) 程序。当你这样做时,你的程序会读取值 12 和“x”,留下“ps”和换行符由 shell 读取,然后 shell 将执行ps 命令(不回显它读取的字符),然后再次提示。

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

【讨论】:

  • 谢谢。我完全错过了问题中的(space),这使我的(现已删除)答案脱轨。现在我不必怀疑我哪里出错了! +1
  • 为什么会读到“X”?
  • @SurajJain:它将读取x,因为它不知道它已完成处理%d 转换,直到它读取不能成为数字一部分的内容(如x) .但是当进程读取x时,任何其他进程都无法读取x——因此shell在输入流中找到ps和换行符并读取这些字符并执行命令。
  • @JonathanLeffler 但是,它不会返回缓冲区吗?
  • @SurajJain:哪个缓冲区?问题中显示的程序与 shell 之间没有共享缓冲区 - 因此进程无法使 x 可用于 shell。
猜你喜欢
  • 1970-01-01
  • 2010-11-30
  • 2017-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-13
  • 1970-01-01
相关资源
最近更新 更多