Consider the following statement:
*((char*)NULL) = 0; //undefined behavior
It clearly invokes undefined behavior. Does the existence of such a statement in a given program mean that the whole program is undefined or that behavior only becomes undefined once control flow hits this statement?
Would the following program be well-defined in case the user never enters the number 3
?
while (true) {
int num = ReadNumberFromConsole();
if (num == 3)
*((char*)NULL) = 0; //undefined behavior
}
Or is it entirely undefined behavior no matter what the user enters?
Also, can the compiler assume that undefined behavior will never be executed at runtime? That would allow for reasoning backwards in time:
int num = ReadNumberFromConsole();
if (num == 3) {
PrintToConsole(num);
*((char*)NULL) = 0; //undefined behavior
}
Here, the compiler could reason that in case num == 3
we will always invoke undefined behavior. Therefore, this case must be impossible and the number does not need to be printed. The entire if
statement could be optimized out. Is this kind of backwards reasoning allowed according to the standard?
The standard states at 1.9/4
The interesting point is probably what "contain" means. A little later at 1.9/5 it states:
Here it specifically mentions "execution ... with that input". I would interpret that as, undefined behaviour in one possible branch which is not executed right now does not influence the current branch of execution.
A different issue however are assumptions based on undefined behaviour during code generation. See the answer of Steve Jessop for more details about that.
An instructive example is
Both current GCC and current Clang will optimize this (on x86) to
because they deduce that
x
is always zero from the UB in theif (x)
control path. GCC won't even give you a use-of-uninitialized-value warning! (because the pass that applies the above logic runs before the pass that generates uninitialized-value warnings)