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 thescanf
call). From n1570 6.3.2.1 p2:In theory, this would allow for something like
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).