未经验证的 scanf 调用会导致未定义的行为吗? [英] Can unverified scanf call cause an undefined behavior?

查看:32
本文介绍了未经验证的 scanf 调用会导致未定义的行为吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

#include int main() {国际我;/* 不确定 */if (scanf("%d", &i) == 1)/* 初始化 */printf("%d\n", i);/* 成功!打印读取值 */别的printf("%d\n", i);/* 输入失败!是否打印`i` UB?*/返回0;}

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

编辑
此外,如果我将 scanf("%d", &i) 替换为 my_initializer(&i):

int my_initializer(int *pi){双room_temp_degc = get_room_temp_in_degc();如果(room_temp_degc <12.0){//凉爽的*圆周率 = 42;返回 1;} 别的 {返回0;}}

解决方案

在 C90 中,这是 UB.

对于 C99 和 C11,从技术上讲,它不是,但输出是不确定的.甚至有可能,紧随其后的另一个 printf 将打印不同的值;未初始化的变量可能会在没有程序的明确操作的情况下发生变化.但是请注意,未初始化的变量只有在其地址已被获取时才能读取*)(此处在 scanf 调用中完成).从 n1570 6.3.2.1 p2:

<块引用>

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

理论上,这将允许类似

int n;&n;printf("%d\n", n);

但编译器仍可能基于第一次读取不会发生在第一次写入之前的假设重新排序语句或分配寄存器,并忽略无副作用的 &n; 语句.>

出于任何实际目的,永远不要读取未初始化的值.首先,你没有理由想要;其次,即使是未指定的值也允许令人惊讶的优化:有些人认为垃圾"值可用于为随机数收集一些熵,这会导致密码软件中出现非常严重的错误,请参见例如王曦的博文.对于偶数示例,其中未初始化的值在与 2 相乘后为奇数,请参见例如这个博客(是的,不确定时间2 只是不确定的,不是偶数,只有其他方面是不确定的).

另见DR 260.

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

使用 DR 338,这是已更正,但不在 C11 之前.添加它是为了在 Titanium 平台上允许 NaT(它存在于寄存器中,但不适用于内存中的值),甚至对于没有陷阱表示的整数类型.我不知道,上面代码中的&n是否对这样的平台有任何影响(严格阅读C11,应该,但我不会依赖它).

Does below snippet invoke undefined behavior in case of an error?

#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;
}

What if scanf fails, is an uninitialized variable accessed?

EDIT
Moreover what if I replace scanf("%d", &i) with 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;
  }
}

解决方案

In C90, this is UB.

For C99 and C11, technically, it isn't, but the output is indeterminate. It's even possible, that another printf directly following will print a different value; uninitialized variables may appear to change without explicit action of the programme. Note, however, that an uninitialized variable can only be read if its address has been taken*) (which is done here in the scanf call). From n1570 6.3.2.1 p2:

If the lvalue designates an object of automatic storage duration that could have been declared with the register storage class (never had its address taken), and that object is uninitialized (not declared with an initializer and no assignment to it has been performed prior to use), the behavior is undefined.

In theory, this would allow for something like

int n;
&n;
printf("%d\n", n);

But compilers may still reorder statements or allocate registers based on the assumption that the first read doesn't occur before the first write and ignore the side-effect free &n; statement.

For any practical purpose, never read uninitialized values. First, there is no reason why you should want to; second, even an unspecified value allows surprsing optimizations: Some thought a "garbage" value could be used to gather some entropy for random numbers which lead to really bad bugs in cryptographic software, see e.g. Xi Wang's blog entry. For an even wierder example, where an uninitialized value is odd after multiplication with 2, see e.g. this blog (yes, indeterminate times 2 is simply indeterminate, not even and only otherwise indeterminate).

See also DR 260.

*) The quoted paragraph is missing in C99, but this should be considered a defect in the standard, not a change in C11. C99 makes it technically defined (for machines without trap representations) to read any uninitialized variable (though their values are still indeterminate and may still appear to change randomly, it's just not UB).

With DR 338, this was corrected, but not before C11. It was added to allow NaT on a Titanium platform (which exists for registers, but not for values in memory), even for integer types without trap representations. I don't know, if the &n in the code above has any effect on such a platform (by a strict reading of C11, it should, but I wouldn't rely on it).

这篇关于未经验证的 scanf 调用会导致未定义的行为吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆