Philip Lantz said:
Tim said:
Philip said:
I think there may be one more kind of pointer value, which is the
kind that caused this thread. It is a value that may appear in a
pointer object as a result of some prior undefined behavior. This
is of course outside the scope of the C standard--which is why it
correctly doesn't appear in your list--but it does occur in actual
implementations.
It behaves much like your type 1, but I'm not sure it's identical;
in particular, its behavior can't be described by referring to the
standard. Before reading this thread, I would not have ever
thought to call it a trap representation, but it is pretty
similar.
Let me see if I can help untangle the description you're giving.
[snip]
Of course, it should go without saying that the above assumes there
has been no previous undefined behavior.
Of course. So why did you write five paragraphs (reiterating stuff I
already know) when I specifically said there was prior undefined
behavior?
My previous posting (ie the response to Ben Bacarisse's post) was,
pretty clearly, only about how the Standard identifies different
kinds of behaviors, not anything more fine-grained. Your response
to that blurs the distinction between the two. I think it's
important to clarify which is which.
As for your question, well, I don't know what you know and what you
don't know, but even if I did, I wasn't writing just for you. What
I'm hoping to do is lay a clear foundation so that the less well
understood aspects can be discussed effectively.
Wait, what? You think that the kind of value I describe,
*specifically caused by prior undefined behavior*, is a subset
of one of your classes,
Yes. The various categories are each definitional, and together
they completely partition the space of pointer values.
which specifically assume there has been no prior undefined
behavior?
Here there is a mistake in logical inference. When I say "the
above assumes there has been no previous undefined behavior",
it means only that _some_ of the previous conclusions might
not hold if there has been previous undefined behavior, not
that they all might not. In any event, the descriptions of
the different classes are definitions, not conclusions.
To be fair I should add that these details may not have been
clear in my earlier writing.
Then why have you guys been talking about it for days?
I haven't. I only followed up to Ben Bacarisse's post because
I thought some people might find it helpful.
However, to clarify my earlier statement, there isn't any point
in talking about what might be true under such circumstances
_without some additional information_ that puts some sort of
bounds on (some cases of) undefined behavior. The key is to
identify which kinds of transgressions now have identifiable
semantics, and what the semantics are in such cases. But
this isn't easy.
The only reason to talk about it is that when someone is
debugging and observes unexpected behavior (behavior that was
unexpected by the one doing the debugging, of course), it
sometimes helps to consider -- in the context of an actual
implementation -- how undefined behavior can lead to the
observed behavior, as a means of working backward to locate the
actual bug. My contributions to this thread have been a vain
attempt to point the discussion in this direction.
In effect what's going on there -- ie, the considering -- is
imagining a more well-defined semantics for cases which are UB as
far as the Standard is concerned. It's important to know both
which cases have more well-defined semantics and what the new
semantics are. For example, on many machines, comparing pointers
using < always works for well-behaved pointer values, even if
they aren't in the same array, and even if one or both is a null
pointer. We might imagine a new semantics where < works with all
pointer values, and returns the right answer for cases the
Standard says are defined, and a "random" answer for the other
cases. So as long as the program doesn't depend in a drastic way
on the results of such pointer comparisons, they are harmless --
nothing bad will happen if there is a stray comparison here and
there (and for which both possible outcomes cause no further
problems).
But: tread cautiously! A similar example might reason that null
pointers have the value zero, and the memory around that location
is accessible, so dereferencing a null pointer is harmless if the
resulting value is ignored. OOPS! This is exactly the kind of
reasoning that led to the famous bug in the Linux kernel, because
gcc relied on the unlimited latitude of earlier undefined behavior.
So, bottom line... I agree that thinking about what might have
happened, undefined-behavior-wise, can be helpful in tracking down
a problem. But is also much harder than thinking about just what
well behaved programs will do, because of the myriad ways that
implementations can (and do!) manifest undefined behavior. And if
in spite of that someone wants to consider the consequences of
"semi-defined" behavior, they better be doggone sure they know
what the heck the implementation is doing (because there is a
pretty good chance it isn't doing what they think it's doing).