Shao Miller said:
status_t some_func(input_t * input, output_t ** output) {
output_t * new_item;
status_t status;
/* Do stuff. Populate 'new_item', 'status' */
if (output) {
/* Caller wants to refer to the result */
*output = new_item;
}
return status;
}
Here, if 'output' does not compare equal to a null pointer, we'll
attempt indirection on it. If the caller passed an argument for
'output' which they believed to be one of {null pointer, pointer to an
object}, then our test here yields a false positive for the latter if
what they actually passed was the third possibility: A non-null pointer
to no object.
Right, because, as I said, checking whether a pointer is null does not
reliably check whether it may be dereferenced. It's a false negative
for the question "May I safely dereference this pointer?". It's not a
false negative for the question "Is this a null pointer?"
The test yields a false negative because it's not a valid test for what
it's trying to test. (No such valid test is possible in portable C.)
Well I didn't quite say that. I was referring to a particular subset of
32-bit pointers. I would certainly consider the representation
0x00000001 to be a trap representation for an 'int *' in 32-bit Windows.
Yes, you did quite say that. You said the pointer type "has no trap
representations".
For VA32 (which would correspond to 'void *', 'char *', etc.), the
reason I would think that is that I've over a decade of use, I guess. I
could be wrong! But here's a starting-point, perhaps:
http://msdn.microsoft.com/en-us/library/k26sa92e.aspx
That looks a lot like the C Standard's requirements for pointer
conversions, with some extra information about how Microsoft's compiler
performs such conversions. Unlike the standard, it doesn't mention trap
representations.
Wait, are you saying that any pointer that does not match one of {null
pointer, points to an object} must necessarily be a trap representation?
I believe so, yes.
Given a pointer value that is neither a null pointer nor a pointer to an
object, what criteria would cause you to claim that it's *not* a trap
representation?
I haven't intentionally backed away from any claims. I wish I knew what
claims you might be referring to.
If you're asking about "null pointer", I thought Mr. Philip Lantz
already answered that when he said: "This traps in the debugger, and the
debugger reports a "null pointer dereference" at address 0x0000000c."
I don't remember that particular statement. It's been a long thread.
/* Let struct foo have a member m at offset 12 (0xc) */
struct foo *ptr = NULL;
do_something_with(foo->m);
The evaluation of `foo->m` has undefined behavior because the value of
`foo` is a null pointer. It's likely that the generated code will take
the value stored in `foo` (0x00000000), add the offset 12 to it
(yielding 0x0000000c), and then attempt to dereference the resulting
pointer value. If the program is being executed under the debugger,
this is likely to cause a trap. The debugger sees an attempt to
dereference address 0x0000000c and, quite reasonably, infers that it was
probably the result of accessing a member of a structure or class, at an
offset of 12 bytes, via a null pointer. The debugger may well have
other information available that makes that inference stronger.
(Conceivably a compiler could generate code to test the value of ptr
against 0x00000000 before attempting to add the offset to it; that could
catch the error slightly sooner and more directly, but at a considerable
performance cost.)
I believe you've been implying that this means that 0x0000000c is
a null pointer. It isn't. Seeing Philip's remark, quoted above,
it's a little clearer why you might have thought so.
[snip]
A pointer with the value 0x0000000c is not a null pointer, nor does it
point to any object. It is, I believe, a trap representation. In
certain contexts, the existence of such a pointer may imply that there's
been an attempt to dereference a null pointer; the null pointer itself
(in this particular implementation) has the representation 0x00000000.
Finally, a correction to something I may or may not have implied
earlier. Creating such a pointer, by evaluating `(void*)0x0000000c`,
does not itself have undefined behavior; 6.3.2.3p5 says that the
conversion may yield a trap representation, not that the conversion
itself has undefined behavior. Any attempt to *use* such a pointer
value does have undefined behavior.
0x0000000c;
/* obviously ok, no UB */
(void*)0x0000000c;
/* Creates a trap representation, then discards it; no UB */
p = (void*)0x0000000c;
/* Attempts to access a trap representation, UB */
if ((void*)0x0000000c) == NULL) ...
/* UB */
It's very likely that the third statement will quietly store the
expected value in `p`, and that the last will evaluate the condition as
false; these are entirely consistent with the behavior being undefined.