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

Moh*_*ain 3 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;
}
Run Code Online (Sandbox Code Playgroud)

如果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;
  }
}
Run Code Online (Sandbox Code Playgroud)

maf*_*fso 6

在C90中,这是UB.

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

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

理论上,这将允许类似的东西

int n;
&n;
printf("%d\n", n);
Run Code Online (Sandbox Code Playgroud)

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

出于任何实际目的,永远不要阅读未初始化的值.首先,没有理由你应该这样做; 第二,即使是未指定的值也允许进行优化:有些人认为"垃圾"值可用于收集随机数的一些熵,这会导致加密软件中出现非常糟糕的错误,例如参见Xi Wang的博客文章.对于一个更加简单的例子,在与2相乘后未初始化的值是奇数,请参见例如此博客(是的,不确定的时间2只是不确定的,不是偶数,只有其他方面是不确定的).

另见DR 260.

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

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

  • 绝对技术上正确的答案,但我应该指出,至少在某些版本中,`gcc -std = c99`已经优化,好像读取不确定的内容是UB:http://blog.frama-c.com/index.php ?柱/ 2013/03/13 /不确定的,不确定的 (3认同)
  • 不,根据DR 338的时间表,在C11中出现的修改后的定义"可能已经用'register`存储类声明"从未进行过C99的任何修订.DR 338在2007-03-09之前提交TC3最终确定,但委员会没有及时就TC3达成协议,而且这一变化出现在C11中.除非您可以访问官方TC3并且您坚持它包含句子(我有N1256草案,我无法在其中的任何地方找到它). (2认同)