问题
While reading this I saw a UB that I don't understand, hoping you can clarify
size_t f(int x)
{
size_t a;
if(x) // either x nonzero or UB
a = 42;
return a;
}
I guess the UB is due to a
not having an initialized value, but isn't that it's defined behavior? Meaning, f(0)
will return the value held by variable a
, whatever it is (I consider this to be something like rand()
). Must we know what value the code snippet returns for the code to have a well-defined-behavior?
回答1:
Meaning, f(0) will return the value held by variable a, whatever it is...
Well, in your case,
a
is automatic local variable- it can have trap representation
- it does not have its address taken.
So, yes, this, by definition causes undefined behavior.
Quoting C11
, chapter §6.3.2.1
[...] 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.
- Related to "why undefined behaviour is undefined", see this post.
- There's a very nice answer related to trap representation and undefined behaviour, check it out.
- Finally, a fine lining between UB and usage of indeterminate values.
回答2:
Supplemental to @SouravGhosh's answer, it is important to understand that having undefined behavior is a property of certain combinations of language constructs and of certain runtime evaluations a program may perform, as specified by the standard. It is not a function of an analysis of what a compiler or program might do; in fact, it is more the opposite: a license to compilers and programs, releasing them from any particular constraint.
Therefore, although the standard is fairly logical and consistent about declaring UB, it is not much useful to approach the question from the direction of questioning why a particular construct has UB or why a particular evaluation may or does exhibit UB. There are reasons for the standard specifying what it does, but the primary answer to why a thing has UB is always "because the standard says so."
回答3:
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.
来源:https://stackoverflow.com/questions/43980679/why-is-returning-a-non-initialized-value-considered-undefined-behavior