Undefined Behavior is a license for an implementation to process code in whatever way the author judges to be most suitable for the intended purpose. Some implementations included logic to trap in cases where an automatic variable was read without having been written first, even if the types otherwise had no trap representations; the authors of the Standard were almost certainly aware of such behavior and judged it useful. The Standard specifies only one situation where things may trap, but only in defined fashion (conversion from a larger integer type to a smaller one); in all other cases where things may trap, the authors of the Standard simply left the behavior Undefined rather than trying to go into any detail about how particular traps work, whether they are recoverable, etc.
Additionally, automatic variables are often mapped to registers that are larger than the variables in question, and even types which don't have trap representations may behave oddly in such cases. Consider, for example:
volatile uint16_t v;
uint32_t x(uint32_t a, uint32_t b)
{
uint16_t temp;
if (b) temp=v;
return temp;
}
If b is non-zero, then temp
will get loaded with v
, and the act of loading v
will cause temp
to hold some value 0-65535. If b
is zero, however, the compiler can't load temp
with v
(because of the volatile qualifier). If temp
had been assigned to a 32-bit register (on some platforms, it might logically be assigned the same one used for a
), the function may behave as though temp
held a value which is larger than 65535. The simplest way for the Standard to allow for such a possibility is to say that returning temp
in the above situation would be Undefined Behavior. Not because it would be expecting that implementations would do anything particularly wonky in cases where the caller ends up ignoring the return value (if the caller was going to use the return value, the caller presumably wouldn't have passed b==0) but because leaving things to implementers' judgment is easier than trying to formulate perfect one-size-fits-all rules for such things.
Modern C implementers no longer treat Undefined Behavior as an invitation to exercise judgment, but rather as an invitation to assume no judgment is required. Consequently, they may behave in ways that can disrupt program execution even if the value of the uninitialized value is used for no purpose except to pass it through code that doesn't know if it's meaningful, to code that ultimately ignores it.