frob said:
In each of these (assignment, conditional, and initializers), they are
considering the NPC as an implementation defined value (7.17),
The special rules for NPC apply to any NPC, not just the one that is
implementation-defined as the expansion of the NULL macro.
... not as a
pointer to an object.
However, I do agree: none of those rules apply to a pointer to an
object; such a pointer would not qualify as an NPC.
... That special value is implementation defined, has no
alignment or representaional requirements specified, and therefore not
necessarily a pointer to void as per the requirement in 6.2.5, p26.
That implementation-defined macro (not value) is required to expand into
a null pointer constant. All null pointer constants, regardless of
whether they're the result of expanding NULL, or produced explicitly by
by user code, are required to convert into null pointers of the
specified type when converted to a pointer type. Null pointer constants
are special in that they implicitly convert into a pointer type under
circumstances where (without the special rules for NPCs) they might not
so convert.
If the explicit or implicit pointer conversion is to 'void*', as is the
case with (void*)0, then the resulting null pointer is a pointer to void.
6.3.2.3p3 specifies that the NPC is unequal to any object or function,
"compares unequal to a pointer to any object or function". When you
leave out the phrases "compares" and "a pointer to", you change the
meaning. An NPC is not only allowed, but required, to compare equal to
the value of an object, if that object has a pointer type and a null value.
... not
that it must be a pointer to void,
I never suggested that all NPCs are required to be pointers to void.
However, when an NPC is converted to a pointer type, the result is a
null pointer of that type; if the specified type is "pointer to void",
it meets the requirements of 6.2.5p26. '(void*)0' is both an NPC in it's
own right, and an NPC (0) converted to a pointer type (void*). It is
therefore both an NPC and an null pointer value. However,
... and 6.3.2.3p5 specifies that a pointer
type may contain data that is not correctly aligned, might not point to an
entity of the given type, and might be a trap representation. So there is
no requirement that every value which can be contained in a pointer to void
must actually be a pointer to void.
'pointer to void' is a type. Whether or not a value is a 'pointer to
void' depends only upon the type of the expression that created the
value; it doesn't depend upon the value itself.
An NPC is a source code construct which when converted to pointer type
results in a null pointer value of the specified type. It is not an
object; only objects can have trap representations. It is a value which
is explicitly described by the standard as being valid for use in a
comparisons for equality, in addition to several other uses. Therefore
it is, in a limited sense, a valid pointer (it is not a valid pointer
for dereferencing, for instance).
The NPC is implementation defined.
No, NPC is standard-defined - paragraph 6.3.2.3p3. The fact that "null
pointer constant" is in italics is how you can identify the fact that
this paragraph constitutes the definition of that term.
It may, or may not, fit the
requiremnents of a pointer to void given in 6.2.5p26.
Agreed - whether or not it fits those requirements depends upon whether
it contains an explicit cast to 'void*'; that cast guarantees that it is
indeed a pointer to void.
As a example, consider a machine requiring even alignment for a pointer to
character.
That's not allowed. The alignment of character types is required to be
'1'. I'll assume, for the sake of your argument, that you're actually
talking about a pointer to a type larger than char.
... Even though the object is capable of storing an odd value, it
does not make the odd value into a pointer to character. (see 6.3.2.3)
Agreed; an arbitrary pointer value is not necessarily correctly aligned
for conversion to a pointer of an arbitrary type. However, any pointer
value can be converted to a pointer to void; 'void' is not allowed to
have alignment restrictions. 'pointer to void' is allowed to have
alignment restrictions, but since we're not talking about pointer
objects, but only pointer values, the alignment restrictions of the
pointer type never come into play. Only the alignment restrictions of
the pointed-at type matter, and 'void' isn't allowed to have any.
In any event, a null pointer of any type may be freely converted to a
null pointer of any other type.
The
NPC could be defined as an odd value, since this satisfies it's requirements
of not comparing equal to a pointer to any object or function given in the
same section. So in this case, the NPC does not fit the requirements for a
pointer to void.
An NPC can't be an odd value, because only arithmetic types are
meaningfully 'odd' or 'even'. An expression with arithmetic type can
qualify as an NPC only if it has a value of 0 and an integer type, and
must therefore be even.
Now, if an NPC doesn't have integral type them it must have the type
'pointer to void', and in principle it could be represented by an
address with odd alignment. However, I can't think of any way in which
portable code could detect the fact. Every operation that depends upon
the alignment of a pointer is meaningless when applied to a null pointer
value.
In practice, if an implementation gives some meaning to the alignment of
a null pointer value, I'd expect it to give it an alignment compatible
with all possible types, just like the other value return values from
malloc(); I'd be (slightly) surprised by any implementation where this
wasn't true.
However, it's alignment and representation is not specified.
Agreed; the type of NULL isn't specified. It can be any integer type, or
'void*', since NULL must expand into an NPC, and those are the only two
options for NPCs. However, the type of (void*)0 is specified by the
standard, section 6.5.4p4: "Preceding an expression by a parenthesized
type name converts the value of the expression to the named type."
Why not? There is no exception stated,
Which would be relevant if it were a definition. Since it's just a list
of examples, the absence of a exception doesn't mean that there aren't
any. Each function has it's own set of valid values, some of which are
explained explicitly, others of which are implicit. If a function is
described as dereferencing a pointer, it's implicitly required to be
dereferenceable. If it's described as comparing the pointer to another
pointer for equality, it's implicitly required to be
equality-comparable. If it's describes as being compared for order with
another pointer, the pair of pointers is required to be comparable for
order. If it's described as writing through the pointer, it's implicitly
required that the destination be writable. printf() makes no such
restrictions on the pointer that matches a "%p" format code, so none of
those implicit requirements apply.
It says "invalid value". The question is whether the not the null pointer
is invalid in this case. I argue that it is not a valid value, therefore an
undefined behavior.
Yes, but you argue invalidly.
Please cite the section of the standard stating that the NPC is a pointer to
void.
I never said that all NPCs are pointers to void. The citation for this
particular NPC is 6.5.4p4.
Yes, a pointer to void can be assigned a value of the NPC, but that does not
make the NPC into a pointer to void.
Correct. What makes it a pointer to void is the explicit cast to
'void*', not the fact that it also qualifies as an NPC.
This is demonstrably false --- the null pointer constant does not
necessarily meet the alignment and representational requirements already
mentioned.
You've got cause and effect reversed. You don't deduce that it's void*
because it meets those requirements; you deduce the fact that it meets
those requirements from the fact that it's required (by 6.5.4p4) to be a
void*.
....
Put another way, a pointer to void may be said to contain a any of these
three:
-- A (properly aligned) pointer to void
How can a pointer to void contain a pointer to void? That way leads to
infinite recursion. In standardese, there's a distinction between the
pointer and the address. The pointer is a C concept; the address is a
machine language concept. A C pointer typically contains a machine
address; in some implementations it contains other things as well. I
think that the standardese corresponding to what you intend to mean here
is "a valid address". However, I'm not sure what you mean. I don't know
how to even talk about the next two "options" you list, until you can
convert what you're talking about into standardese.
....
Again, please cite that requirement.
6.5.4p4.
The NPC is implementation defined,
Incorrect: the rules for NPCs (plural, NOT singular) are
standard-defined in 6.3.2.3p3. The particular NPC that 'NULL' expands
into is implementation-defined, but NPCs in general aren't.
... and
needn't be valid in any particular type;
Per that section, it must either be an integer expression with a value
of 0, in which case it has an integer type, or it must be such an
expression, converted to 'void*', in which case it has type 'void*'.
There's no other options allowed by that section.
6.5.4p4.