(e-mail address removed) (Nitin Bhardwaj) wrote: (re C99 3.4.3 and 3.4.4)
So it boils down to the following :
UNDEFINED : The implementor can break into your house, the *Standard*
can't do anything about it;;Then why does the compiler allow such thing(s)
to get compiled in the first place ???
Because the compiler cannot detect all instances of undefined
behavior at compile time. Take the following example:
int foo(int *p)
{
return p[50];
}
How is the compiler to know wether or not this function invokes
undefined behavior?
It will invoke undefined behavior if p is NULL, or p is not a valid
pointer, or p is a valid pointer, but points to a block of memory that
is not large enough to hold 51 int's.
Not large enough or not correctly (sufficiently) aligned.
Or, technically, where that memory was last stored as another type
(possibly as bytes) creating a representation that is not a valid int
representation -- but there are few if any platforms that actually
have trap representations in integer types, though it is allowed
except for unsigned char.
Or, for completeness sake, the actual object is volatile, but that
qualifier was cast away at some intermediate point.
It will invoke unspecified behavior if p points to a block of memory
that is large enough, but for which the 51's element has not been
initialized or assigned a known value.
Use of an indeterminate (uninitialized) value is also Undefined
Behavior; besides the theoretical possibly of a trap represenation, it
would also be legal and possibly valuable (though costly without
special hardware, which is arguably costly in its own way) for an
implementation to detect and diagnose use of uninitialized memory.
The only Unspecifieds that I can see applying here are:
- p[50] is part of a member of a union when the last store was to a
different member (in practice, a differently typed one) but did not
create a trap representation -- maybe, this one is not crystal clear;
or was derived (without other UB like overflow) from one or more bytes
(or parts) of a representation that includes padding bytes, union
"residue" bytes, or padding bits in a type that allows and uses them;
or multiple representations, or negative zero, if used.
- or for that matter, on the vanishingly few machines that have
(integer) negative zero some (specified) operations may unspecifiedly
produce it instead of positive; but *used as an int value* it gives
the same results as positive, and so mostly doesn't matter.
- p[50] was affected by order of side effects in initialization of an
aggregate variable or compound literal, or of subexpression (operand)
or function argument evaluation; in particular if
int bar (int *p) { return p[50] = 42; }
int quux (int a, int b) { return a; }
.... int x[51] = {0}; ... quux (foo(p), bar(p)) ...
unspecifiedly produces 0 or 42 -- at least (I think) in C90 with
DR087, though not AFAICS explicitly reflected in C99, except probably
in the attempted "formal model" stuff that didn't make it. (And note
this depends on the sequence points on the calls to foo and bar; if
those were just expressions, or macros, you're back to Undefined.)
- p[50] contains a value produced by certain library functions such as
ftell() or time(), converted down if needed; but these are almost
certainly documented even though not mandated by the standard.
- David.Thompson1 at worldnet.att.net