【问题标题】:scanf in c and relation input bufferc中的scanf和关系输入缓冲区
【发布时间】:2016-01-21 18:08:31
【问题描述】:

我尝试了解 scanf 和输入缓冲区之间的关系。我使用带有以下格式字符串的 scanf:

int z1,z2;
scanf("%d %d", &z1,&z2);

并尝试理解为什么我在输入 54 之类的数字并按 Enter 后可以输入尽可能多的空格(Enter、Blanks、Tabs)。

据我了解,我按下的每个键都会放入输入缓冲区,直到我按下 Enter。

因此,如果我输入 54 并按 Enter,则输入缓冲区包含 3 个元素、两位数字和换行符。所以我的缓冲区看起来像 [5][4][\n]

现在 scanf/formatstring 从左到右进行评估。所以第一个 %d 匹配到 54,54 存储在 z1 中。

由于格式字符串中的空格,按第一次回车引起的换行符(\n)被“消耗”了。

因此,在对第一个 %d 和空格 (\n) 求值后,缓冲区再次为空。

现在 scanf 尝试评估格式字符串中的第二个(也是最后一个)%d。 因为缓冲区现在是空的,所以 scanf 等待进一步的用户输入(在我的案例键盘中,用户输入 = 从标准输入读取)。

所以缓冲区状态/动作序列是

buffer empty -> call of scanf -> scanf blocks for user input --> user input is: 54 Enter --> buffer contains: [5][4][\n] -> 评估第一个 %d -> 缓冲区包含 [\n] -> 评估空白 -> 缓冲区为空 --> 用户输入的 scanf 块(因为第二个和最后一个 %d 的评估) --> ...

我理解正确吗? (对不起,英语不是我的母语)

问候

【问题讨论】:

  • 如果您使用终端,那么终端对输入的处理也会发挥作用;通常程序只会发送完整的输入行,但这不是必须的。使用来自文件的输入重定向来讨论 程序的 行为会更容易,例如 prog < prepared-inp.txt 在 Windows 命令 shell 和各种 linux shell 中的工作方式类似。
  • 另请注意,“%d%d”格式字符串的行为完全相同;当遇到第一个非数字时(其中是像换行符这样的空格)解析第一个整数停止,并且(第二个)%d 格式正确地跳过它遇到的所有空格,因为它只对数字感兴趣。格式字符串中的空格仅对%c%[...] 有意义(否则会将其分配给它们相应的参数)。
  • 我也不喜欢以“缓冲区”范例开头,因为可能没有任何缓冲区,或者可能有任意数量的缓冲区(在键盘中,在远程终端/计算机中,在您的接入点,在您的网卡中,在您的程序中。后者可能就是您的意思(您可以使用setbuf() 更改),但您可以禁用它!)有趣的是字符序列getchar() 看到。 它们究竟来自哪里以及它们是否被缓冲在某个地方是次要的。
  • @PeterSchneider 空白对于%n 也很重要。
  • @CoolGuy 您的意思是输入中的空格[未]被跳过-因此在分配值之前[未]计数-取决于%n之前的空格格式?真的。当然;-)。

标签: c scanf


【解决方案1】:

差不多。

Scanf 读取输入缓冲区 (stdin)。

在 windows cmd.exe 终端中按 Enter 将您输入的输入刷新到输入缓冲区中,提示您的第一个变量被填充。

然后它再次提示填充第二个变量。

【讨论】:

  • scanf 不会在 %c%[..] 转换的空白处停止。
【解决方案2】:

据我所知,我按下的每个键都会放入输入缓冲区,直到我按下 Enter。

正确。按 Enter 将数据刷新到stdin(标准输入流)。请注意,它还将\n 发送到stdin

因此,如果我输入 54 并按 Enter,输入缓冲区包含 3 个元素、两位数字和换行符。所以我的缓冲区看起来像 [5][4][\n]

是的。

现在 scanf/formatstring 从左到右进行评估。所以第一个 %d 匹配到 54,54 存储在 z1 中。

没错。

由于格式字符串中的空格,按第一次回车引起的换行符(\n)被“消耗”了。

正确。

因此,在对第一个 %d 和空格 (\n) 求值后,缓冲区再次为空。

是的。

现在 scanf 尝试评估格式字符串中的第二个(也是最后一个)%d

不完全是。

两个%d之间的空格是whitespace characterscanf格式字符串中的空白字符指示scanf扫描并丢弃所有空白字符,如果有的话,直到第一个非空白字符。这可以在 C11 标准委员会草案 n1570 中看到:

7.21.6.2 fscanf 函数

[...]

  1. 由空白字符组成的指令通过读取输入直到第一个非空白字符(仍然未读取)或直到无法读取更多字符来执行。该指令永远不会失败。

这意味着执行仍在%ds 之间的空格中,因为它还没有遇到非空白字符。

因为缓冲区现在是空的,所以 scanf 等待进一步的用户输入(在我的情况下,用户输入 = 从标准输入读取键盘)。

是的。

所以,

缓冲区空 -> 调用 scanf -> 用户输入的 scanf 块 -> 用户输入是:54 Enter -> 缓冲区包含:[5][4][\n] -> 评估第一个 %d - -> 缓冲区包含 [\n] -> 评估空白 --> 缓冲区空 --> 用于用户输入的 scanf 块(因为第二个和最后一个 %d 的评估)--> ...

应该是

“缓冲区空 -> 调用 scanf -> scanf 块用于用户输入 -> 用户输入是:54\n -> 缓冲区包含:54\n -> 评估第一个 %d - -> 缓冲区包含\n -> 评估空白--> 缓冲区为空--> 用于用户输入的scanf 块(因为评估了空白)--> ..."


请注意,当%ds(%d 之前)之间有许多空白字符或没有空白字符时,scanf 的行为方式相同,因为%d 已经跳过了前导空白字符。事实上,唯一对空白字符有意义的格式说明符是 %c%[%n,如 n1570 所示:

7.21.6.2 fscanf 函数

[...]

  1. 输入空白字符(由isspace 函数指定)将被跳过,除非该规范包含[cn 说明符。 284

【讨论】:

    猜你喜欢
    • 2015-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-19
    • 2021-07-11
    • 1970-01-01
    • 2016-01-17
    • 1970-01-01
    相关资源
    最近更新 更多