在while循环中使用scanf

15

这可能是一个非常简单的问题,有一个非常简单的答案:

我正在阅读Pratta的《C Primer Plus》, 他一直在使用这个例子

while (scanf("%d", &num) == 1)...

== 1是真的必要吗?好像可以直接写成:

while (scanf("%d", &num))

看起来这个等式测试是不必要的,因为scanf返回读取的对象数,而1会使while循环为真。这样做的原因是确保读取的元素数量恰好为1还是完全多余的?


是的,对我来说看起来非常冗余... - tzaman
5个回答

28
在C语言中,0被评估为假(false),而其他所有值都被评估为真(true)。因此,如果scanf返回EOF(一个负值),循环将被评估为真(true),这不是你想要的结果。请注意保留原文中的HTML标签。

EOF是关键元素,很好的捕捉。 - Tyler Brock

10

由于scanf在文件结束时返回值EOF(即-1),因此原始循环是正确的。只要输入包含与%d匹配的文本,它就会运行,并在第一个不匹配或文件结束时停止。

如果scanf期望多个输入,一眼就可以看出更清楚....

while (scanf("%d %d", &x, &y)==2) { ... }

当第一次无法匹配两个值时,循环将退出,这可能是由于文件结束(scanf返回EOF(-1))或输入匹配错误(例如,输入xyzzy 42%d %d不匹配, 因此scanf在第一个失败处停止,并返回0,而不写入任何一个变量x或y),当它返回小于2的某个值时。

当然,当从普通人那里解析真实的输入时,scanf不是你的朋友。它在处理错误情况时存在许多陷阱。

编辑: 更正了一个错误: scanf在文件结束时返回EOF,或者返回成功设置变量数的非负整数。

关键点是,由于在C语言中任何非零值都被认为是TRUE,因此在类似这样的循环中未正确测试返回值可能会导致意外行为。特别是,while(scanf(...))是一个无限循环,除非遇到不能按其格式转换的输入文本。

我再次强调,scanf不是你的朋友。使用fgetssscanf的组合可能足以进行一些简单的解析,但是即使在这种情况下,也很容易被边缘情况和错误所困扰。


3

您正确理解了C代码。

有时测试读取的项目数量的原因是有人想确保所有项目都被读取,而不是scanf在输入与预期类型不匹配时提前退出。 在这种特殊情况下,这并不重要。

通常,scanf是一个糟糕的函数选择,因为它不能满足来自人类用户的交互式输入的需求。 通常,fgets和sscanf的组合会产生更好的结果。 在这种特殊情况下,这并不重要。

如果以后的章节解释了为什么某些编码实践比这个微不足道的例子更好,那就很好。 但如果没有,你应该放弃你正在阅读的书。

另一方面,您的替代代码并不完全是替代品。 如果scanf返回-1,则while循环将执行。


错误,查看其他答案。当遇到EOF或读取错误时,它返回-1。 - Adam Rosenfield
2
错误的,看看其他答案。-- 你的意思是错了,看看我的答案,因为这就是我写“如果scanf返回-1,则while循环将执行”的原因。 - Windows programmer

1

虽然严格来说它并不是必要的,但有些人出于某些原因更喜欢这样做。

首先,通过与1进行比较,它变成了一个显式的布尔值(true或false)。如果没有进行比较,你就是在测试一个整数,在C语言中是有效的,但在后来的语言(如C#)中则无效。

其次,有些人会以 while([function]) 的方式阅读第二个版本,而不是 while([return value]),并且在测试函数时会暂时感到困惑,而明显的意图是测试返回值。

这完全可以是个人喜好的问题,就我而言,两者都是有效的。


1

可能可以不使用显式比较来编写它(尽管请参见JRL的答案),但为什么要这样做呢?我认为,只有具有明确布尔语义的值(例如isdigit()调用)才应该使用无比较条件。其他所有情况都应该使用显式比较。在这种情况下(scanf的结果),语义明显不是布尔值,因此需要显式比较。

此外,通常可以省略的比较通常是与进行的比较。当您想省略与其他内容(如本例中的1)进行比较时,最好再三考虑并确保您知道自己在做什么(再次参见JRL的答案)。

无论如何,当可以安全地省略比较并且您实际上省略了它时,条件的实际语义含义仍然相同。如果您担心的是代码的效率,那么它绝对不会影响结果代码的效率。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接