3

请看一下这个代码片段:

char line1[10], line2[10];
int rtn;
rtn = scanf("%9[a]%9[^\n]", line1, line2);
printf("line1 = %s|\nline2 = %s|\n", line1, line2);
printf("rtn = %d\n", rtn);

输出:

$ gcc line.c -o line
$ ./line
abook
line1 = a|
line2 = book|
rtn = 2
$./line
book
line1 = |
line2 = �Js�|
rtn = 0
$

对于输入abook,从%9[a]处失败并存储先前解析的+处。 然后解析剩余的行并存储刚才解析的+ at 。 请注意这里的2点: bbooka\0line1
%9[^\n]book\0line2

  1. 在存储解析后的输入时,由于是字符串的转换说明符 \0,因此会在其末尾附加。%[]
  2. %9[a]失败时,b没有scanf退出。它只是继续扫描进一步的输入。

现在对于 input book,应该从%9[a]失败并且应该存储在因为这里没有被解析。然后应该解析剩余的行并且应该存储刚刚解析的+ at 。 bbook\0line1%9[^\n]book\0line2

现在,让我们看看到底发生了什么:
这里的返回值为 0,这意味着scanf没有为任何变量赋值。scanf简单地退出而不分配任何值。所以垃圾数据在line2。在line1这种垃圾数据的情况下,恰好是一个NULL字符。

但这很奇怪!不是吗?
我的意思是如果在输入的第一个字符处失败则scanf退出。%[...](即使scanf语句中存在额外的转换说明符。)
但是如果在%[...]除第一个字符之外的任何其他字符处同样失败,则scanf只需继续扫描进一步的输入。(当然,如果有额外的转换说明符。)它不会退出。

那么为什么会出现这种不一致呢?为什么不让scanf语句继续扫描输入(如果当然有额外的转换说明符)即使%[...]在输入的第一个字符处失败?就像在其他情况下发生的一样。
这种不一致的背后有什么特殊原因吗?

$ gcc --version
gcc (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3
4

2 回答 2

4

2)%9[a]在b处失败时,scanf没有退出。它只是继续扫描进一步的输入。

是的,该%9[a]指令的意思是“最多存储9'a'秒,但至少存储一个(1),所以转换%9[a]没有失败,它成功了。它发现的'a's 比它可能消耗的要少,但这并不是失败。输入匹配失败'b',但转换成功。

(1)在 7.21.6.2 (12) 中指定,其中描述了转换:

[ 匹配一组预期字符(扫描集)中的非空字符序列

现在对于输入书,%9[a]应该在书的 b 处失败,并且应该只存储'\0'在 line1 处,因为这里没有解析任何内容。然后%9[^\n]应该解析剩余的行,并且应该存储刚刚解析book+\0的第 2 行。

不,它应该在转换失败时退出。第一次转换%9[a]失败,所以scanf应该停止并返回 0,因为没有转换成功。

始终检查scanf.

这是在 7.21.6.2 (16) 中指定的(对于fscanf,但scanf等效于fscanfwithstdin作为输入流):

如果在第一次转换(如果有)完成之前发生输入错误,则该fscanf函数返回宏的值。否则,该函数返回分配的输入项的数量,如果发生早期匹配失败,该数量可能少于提供的数量,甚至为零。EOF

这里的输出line1是什么,这正是我们所期望的。一个空字符串!

你不能指望什么。数组line1line2没有初始化,所以当转换失败时,它们的内容仍然是不确定的。在这种情况下,line1第一个 0 字节之前不包含可打印字符。

但是因为line2它是垃圾字符!我们没想到会这样。那么这是怎么发生的呢?

这就是line2. 从来没有为元素分配任何值,所以它们是在调用scanf.

于 2013-05-27T17:39:48.870 回答
1

从评论转移到问题,因为对回复问题的回复需要比评论允许的更多空间。

此注释指的是代码的早期版本:

由于您没有检查 的返回值scanf(),因此您不知道它是否说“我失败了”。当您忽略它的错误返回时,您不能责怪它;在第二个例子中,它会说“0 个项目扫描成功”,这意味着没有任何变量被设置为任何有用的东西。您必须始终检查返回值,scanf()以便知道它是否符合您的预期。

回复问题是:

我更新了代码和输出以显示scanf. 是的,对于案例 2,返回值为 0。但这并不能回答问题。显然scanf在案例 2 中退出。但对于案例 1,返回值为 2,这意味着scanf成功为两个变量赋值。那么为什么会出现这种不一致呢?

我没有看到任何不一致之处。fscanf()规范(从 ISO/IEC 9899:2011 复制,但 URL 链接到 POSIX 而不是 C 标准)说:

¶3 [...] 每个转换规范都由字符%. 之后%,依次出现以下内容:

— 一个可选的赋值抑制字符 *。
— 一个大于零的可选十进制整数,用于指定最大字段宽度(以字符为单位)。
— 一个可选的长度修饰符,指定接收对象的大小。
— 一个转换说明符字符,指定要应用的转换类型。

后来,它说:

¶8 [...] 输入的空白​​字符(由isspace函数指定)被跳过,除非规范包含一个[, c, 或n说明符。284)

¶9 从流中读取输入项,除非规范包含n说明符。输入项被定义为最长的输入字符序列,它不超过任何指定的字段宽度,并且是匹配输入序列的前缀。285) 输入项之后的第一个字符(如果有)保持未读状态。如果输入项的长度为零,则指令执行失败;这种情况是匹配失败,除非文件结尾、编码错误或读取错误阻止了来自流的输入,在这种情况下,它是输入失败。

¶12 [...]

[匹配一组预期字符(扫描集)中的非空字符序列。286)

[加粗斜体强调。我保留了脚注参考,但脚注的内容对讨论并不重要,所以我省略了它们。]

因此,您所看到的行为正是标准所要求的。当%9[a]应用于字符串abook时,有一个与转换规范a匹配的序列%9[a],因此指令成功,扫描继续book。当%9[a]应用于字符串book时,有零个字符匹配该项目,因此指令执行失败,这是一个匹配错误,因为它是第一个转换规范,所以返回值 0 是正确的。

请注意,长度指定了最大字段宽度,因此9in%9[a]表示 1-9 个字母a

于 2013-05-27T20:40:37.777 回答