Can unverified scanf call cause an undefined behavior?

江枫思渺然 提交于 2020-01-30 06:07:20

问题


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

回答1:


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).



来源:https://stackoverflow.com/questions/27104910/can-unverified-scanf-call-cause-an-undefined-behavior

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!