scanf("%d%d", &x, &x)的行为是否定义良好?

16

以下代码是否定义良好?

#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.2 4


3
与限制无关,这很清晰明确。您将一个值写入两次到同一地址,因此您会得到最新的那个值。 - Guy Sirton
1
@Pascal Cuoq 如果这是一个重复问题,请告知。我已经搜索了15分钟,但如果是的话也不会感到惊讶。 - chux - Reinstate Monica
2
@nneonneo 你确定你理解了restrict的工作原理吗?请查看C11 6.7.3.1:4中的“并且X也被修改”这句话。 - Pascal Cuoq
@PascalCuoq:你是正确的。第10个例子明确说明,如果两者都没有被修改,则受限指针可以指向相同的对象。因此我撤回我的观察。 - nneonneo
1
考虑使用 scanf 的等效情况。如果您使用 newline 分隔两个输入,您是否期望它们被颠倒? - Weather Vane
显示剩余6条评论
3个回答

13

简短的回答是:是的,它被定义了

scanf将尝试将从stdin读入的以十进制写成的可选起始空格和可选符号的字节序列转换为整数。如果成功,该数字将被存储到x中。 scanf然后会执行这些步骤第二次。返回值可以是EOF012,对于后两个值,最后一个转换的数字将已被存储到x中。

长的回答则稍微有些微妙:

C标准似乎确实指定值按照格式字符串的顺序存储。引用C11标准:

7.21.6.2 fscanf函数

...

4 fscanf函数按照格式中每个指令的顺序依次执行。当所有指令都已执行完毕,或者如果某个指令失败(如下所述),则函数返回。

...

7 一个作为转换说明符的指令定义了一组匹配的输入序列,对于每个说明符,下面的内容描述了这些匹配的输入序列。转换说明符按照以下步骤执行:

...

10 除了%说明符之外,输入项(或在%n指令的情况下,输入字符的计数)将被转换为适合于转换说明符的类型。如果输入项不是匹配序列,则指令执行失败:这个条件是一个匹配失败。除非通过*指示了赋值抑制,否则转换的结果将放入紧随格式参数后面、尚未收到转换结果的第一个参数所指向的对象中。

...

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

在此规范中没有提到任何其他访问输出对象的地方。

然而,标准的措辞似乎表明,如果2个指针指向同一对象,则行为可能是意外的:转换的结果将放入紧随格式参数后面、尚未收到转换结果的第一个参数所指向的对象中。这个短语有些模糊不清: 尚未收到转换结果的第一个参数所指向的对象是指什么?对象还是参数?对象会接收转换结果,而不是指针参数。在你扭曲的示例中,对象x已经接收了一个转换结果,因此它不应该再接收另一个转换结果...但正如supercat指出的那样,这种解释过于严格,因为它将意味着所有转换的值都存储在第一个目标对象中。

因此,它似乎是完全指定并且定义良好的,但规范的措辞可能需要完善以消除潜在的歧义。


这个措辞是指当迭代指令时,参数是如何迭代的。 - autistic
2
你的答案的关键要素是:按顺序执行每个格式指令和**转换结果放置在第一个参数所指向的对象中...**。与@Seb的评论一起,指出这是需要得出结果定义良好的规范。需要注意的是,这是参数顺序(而不是值)的问题。 - chux - Reinstate Monica
1
从语法上讲,短语“...在格式参数后的第一个参数尚未接收到转换结果”表明,为了确定“第一个”参数,认为参数已经接收到结果。否则,所有结果都将存储到格式说明符后面的第一个参数中,因为没有参数会接收结果。如果标准说“第一个参数...其目标对象尚未接收到结果”,那就是另一回事了。 - supercat
@supercat:说得好。我更新了答案,以限制这个挑剔的解释的价值。 - chqrlie

3
< p > scanf()家族函数会严格按照格式字符串中的顺序执行指令。因此第一个值将被读取,然后是第二个值,覆盖第一个值。这里没有任何未定义行为。


2

是的,定义清晰明了。它的意思是“将第一个令牌读入*dest,然后再将第二个令牌再次读入*dest”。这很奇怪,但是合法的。 是的,因为sscanf()按照格式字符串中严格的顺序执行指令。


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