Can unverified scanf call cause an undefined behav

2019-02-25 03:42发布

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条回答
叛逆
2楼-- · 2019-02-25 04:12

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

查看更多
登录 后发表回答