未经validation的scanf调用会导致未定义的行为吗?
以下片段是否在发生错误时调用未定义的行为?
#include 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失败怎么办?是否访问了未初始化的变量?
编辑
此外,如果我用my_initializer(&i)
替换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; } }
在C90中,这是UB。
对于C99和C11,从技术上讲,它不是,但输出是不确定的。 甚至可能,直接跟随的另一个printf
将打印不同的值; 如果没有程序的明确行动,未初始化的变量可能会发生变化。 但是请注意,只有在已经采用*)的情况下才能读取未初始化的变量(这在scanf
调用中完成)。 从n1570 6.3.2.1 p2:
如果左值指定了一个自动存储持续时间的对象,该对象可以使用
register
存储类声明(从未使用其地址),并且该对象未初始化(未使用初始化程序声明,并且在使用之前未对其进行任何赋值) ),行为未定义。
理论上 ,这将允许类似的东西
int n; &n; printf("%d\n", n);
但是编译器仍然可以根据第一次写入之前没有发生第一次读取的假设重新排序语句或分配寄存器,并忽略副作用free &n;
声明。
出于任何实际目的,永远不要阅读未初始化的值。 首先,你没有理由想要; 第二,即使是一个未指定的值也允许进行优化:有些人认为可以使用“垃圾”值来收集随机数的一些熵,这会导致加密软件中出现非常糟糕的错误,例如参见Xi Wang的博客文章 。 对于一个更加狡猾的例子,其中未初始化的值在乘以2之后是奇数,请参见例如此博客 (是的,不确定的时间2只是不确定的,不是偶数,只有其他方面是不确定的)。
另见DR 260 。
*) C99中缺少引用的段落,但这应该被视为标准中的缺陷,而不是C11的变化。 C99使其在技术上定义(对于没有陷阱表示的机器)来读取任何未初始化的变量(尽管它们的值仍然是不确定的,并且可能仍然看起来随机变化,它不是UB)。
使用DR 338 ,这已得到纠正,但不是在C11之前。 它被添加为允许在Titanium平台上存在NaT
(存在于寄存器中,但不存在于内存中的值),即使对于没有陷阱表示的整数类型也是如此。 我不知道,如果上面的代码中的&n
对这样的平台有任何影响(通过严格阅读C11,它应该,但我不会依赖它)。