未经验证的scanf调用会导致未定义的行为吗?

时间:2014-11-24 12:29:21

标签: c undefined-behavior c11

如果出现错误,下面的代码段会调用未定义的行为吗?

#include <stdio.h>

int main() {
    int i;                      /* Indeterminate */
    if (scanf("%d", &i) == 1)   /* Initialize */
        printf("%d\n", i);      /* Success! Print read value */
    else
        printf("%d\n", i);      /* Input failed! Is printing `i` UB or not? */
    return 0;
}

如果scanf失败,是否访问了未初始化的变量?

修改
此外,如果我将scanf("%d", &i)替换为my_initializer(&i)

,该怎么办?
int my_initializer(int *pi)
{
  double room_temp_degc = get_room_temp_in_degc();
  if(room_temp_degc < 12.0) {
    // Cool
    *pi = 42;
    return 1;
  } else {
    return 0;
  }
}

1 个答案:

答案 0 :(得分:6)

在C90中,这是UB。

对于C99和C11,从技术上讲,它不是,但输出是不确定的。它甚至可能,直接跟随的另一个printf将打印不同的值;如果没有程序的明确行动,未初始化的变量似乎会发生变化。但请注意,只有在其地址已被 *)(这是在scanf调用中完成)时,才能读取未初始化的变量。从n1570开始6.3.2.1 p2:

  

如果左值指定了一个自动存储持续时间的对象,该对象可能已使用register存储类声明(从未使用过其地址),并且该对象未初始化(未使用初始化程序声明且未分配给它已在使用前执行),行为未定义。

理论上,这将允许像

这样的东西
int n;
&n;
printf("%d\n", n);

但是编译器仍然可以根据第一次读取之前没有发生的第一次读取并忽略副作用免费&n;语句来重新排序语句或分配寄存器。

出于任何实际目的,永远不要阅读未初始化的值。首先,没有理由你应该这样做;第二,即使是一个未指定的值也可以进行超额优化:有些人认为&#34;垃圾&#34;值可用于收集随机数的一些熵,这会导致加密软件中的错误,例如, Xi Wang's blog entry。对于一个更加平坦的例子,在乘以2之后未初始化的值是奇数,参见例如this blog(是的,不确定的时间2只是不确定的,不是偶数,只有其他方面不确定)。

另见DR 260

*) C99中缺少引用的段落,但这应该被视为标准中的缺陷,而不是C11中的变化。 C99使其在技术上定义(对于没有陷阱表示的机器)来读取任何未初始化的变量(尽管它们的值仍然是不确定的,并且可能仍然看起来随机变化,它只是不是UB)。

使用DR 338,这已得到纠正,但不是在C11之前。它被添加到Titanium平台上允许NaT(存在于寄存器中,但不存在于内存中的值),即使对于没有陷阱表示的整数类型也是如此。我不知道,如果上面代码中的&n对这样的平台有任何影响(通过严格阅读C11,它应该,但我不会依赖它)。< / p>