以下代码是否定义良好?
#include <stdio.h>
int ScanFirstOrSecond(const char *s, int *dest) {
return sscanf(s, "%d%d", dest, dest);
}
int main(void) {
int x = 4;
ScanFirstOrSecond("5", &x);
printf("%d\n", x); // prints 5
// Here is the tricky bit
ScanFirstOrSecond("6 7", &x);
printf("%d\n", x); // prints 7
return 0;
}
换句话说,...
参数是否隐含restrict
?
我发现的最适用的C规范是
fscanf函数依次执行格式的每个指令。 ......C11dr§7.21.6.24
答案 0 :(得分:11)
简短回答是:是,已定义:
scanf
将尝试将stdin
中的字节序列转换为基数为10的整数,并带有可选的初始空格和可选符号。如果成功,该号码将存储到x
。然后scanf
将再次执行这些步骤。返回值可以是EOF
,0
,1
或2
,对于后者2,转换的最后一个数字将存储到x
中。
长的回答有点夸张:
似乎C标准确实指定了值以格式字符串的顺序存储。引用C11标准:
7.21.6.2
fscanf
函数...
4
fscanf
函数依次执行格式的每个指令。当所有指令都已执行,或者指令失败时(如下所述),函数返回。...
7作为转换规范的指令定义了一组匹配的输入序列,如下面针对每个说明符所述。转换规范按以下步骤执行:
...
10除了
%
说明符的情况外,输入项(或者,在%n
指令的情况下,输入字符数)转换为适合转换的类型符。如果输入项不是匹配序列,则指令的执行失败:此条件是匹配失败。除非*
指示赋值抑制,否则转换的结果将放在由尚未收到转换结果的format参数后面的第一个参数指向的对象中。...
16如果在第一次转换(如果有)完成之前发生输入故障,
fscanf
函数将返回宏EOF
的值。否则,该函数返回分配的输入项的数量,如果早期匹配失败,则可以少于提供的数量,甚至为零。
本规范中的任何其他地方都没有对提到的输出对象的任何访问。
然而,标准的措辞似乎表明如果2个指针指向同一个对象,则行为可能是意外的:转换的结果放在格式后面的第一个参数所指向的对象中尚未收到转换结果的参数。这句话有些含糊不清:尚未收到转化结果指的是什么?对象或论点?对象接收转换结果,而不是指针参数。在您的扭曲示例中,对象x
已经收到转换结果,因此它不应该接收另一个...但正如supercat所指出的那样,这种解释具有明显的限制性,因为它意味着存储所有转换后的值进入第一个目标对象。
所以它看起来完全明确并且定义明确,但可以完善规范的措辞以消除潜在的歧义。
答案 1 :(得分:3)
scanf()
族函数严格依次执行您在格式字符串中保留的指示。因此,第一个值将被读入,然后第二个值将覆盖第一个值。这里没什么UB。
答案 2 :(得分:2)
是的,定义明确。这意味着“将第一个令牌读入* dest,然后再将第二个令牌读入* dest”。这很奇怪但合法。 是的,因为sscanf()按严格的顺序执行格式字符串中的指令。