On 12/18/2012 08:25 AM, Szabolcs Nagy wrote:
> it seems the definition of 'null pointer constant'
> relies on integer constant expressions (6.3.2.3p3)
>
http://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p3
>
> so eg.
>
> (void*)((char*)1 - 1)
>
> is not a null pointer constant because pointer
> arithmetics cannot give an integer constant expression
>
> but at least this could be used in pointer initializers
> since address constants are allowed there (6.6p7)
Two things to keep in mind:
When converting an arbitrary integer to a pointer type "the result is
implementation-defined, might not be correctly aligned, might not point
to an entity of the referenced type, and might be a trap
representation." (6.3.2.3p1)
Now, alignment cannot be an issue with char*, but trap representation
certainly is. Unless the compiler you're using tells you something
useful about where (char*)1 points, you're asking for big trouble trying
to use it.
Subtraction of an integer from a pointer is defined only for a pointer
that points at an element of an array, or one past the end of the array,
and only if the defined result of the subtraction would also be in that
array (6.5.6p8). Unless the compiler you're using defines that (char*)1
points at an element of an array, other than the first element of that
array, the subtraction has undefined behavior. If your compiler does
provide such a guarantee, then you're right, this could be used as a
pointer constant expression.
However, I get the impression that you were expecting
(char*)1-1 == (char*)0.
The standard says nothing of the kind - in fact, if that expression has
defined behavior, it's guaranteed to be false. Here's why: if the
implementation does define (char*)1 as pointing at an element, other
than the first, of an array, then (char*)1 - 1 will point at the
immediately preceding element of the array, and therefore, points at an
object. Now (char*)0 is a null pointer, and a null pointer can never
compare equal to a pointer to an object (6.3.2.3p3). Therefore, if the
implementation-defined value of (char*)1 is such as to give the
subtraction standard-defined behavior, that behavior is guaranteed to
NOT result in a pointer that compares equal to (char*)0.
Correct. But it is a null pointer with the type 'void*'.
> it is mildly unexpected that this cannot be used as a
> null pointer constant
> (this matters when determining the type of ?: (6.5.15)
I may be missing something, but as far as I can tell, the rules given in
6.5.15p6 give the same result whether the second or third operand is a
null pointer constant, or merely a constant expression with the type
void* and a null value. Could you provide a counter-example?
However, that difference does make one case a constraint violation. The
expression
condition ? null_pointer_constant : function_pointer
is well-defined, and has the type of the function pointer, but
condition ? constant_null_pointer_to_void : function_pointer
is a constraint violation. (6.5.15p3)
> expressions, assigning it to function pointers (6.5.16.1)
> or comparing it to function pointers (6.5.9))
You're right on those two counts; both cases are constraint violations,
but would not be if it qualified as null pointer constant.
> but (for me at least) it's more surprising that this is
> not even a constant expression that can be used in
> initializers, ie.
>
> void *p = 0?(void*)0:(void*)0;
>
> is undefined behaviour if i read section 6.6 correctly
> (not even a constraint violation that requires a diagnostic)
That does seem a bit surprising, but I believe you're correct. Note,
however, that a constant expression is required only if 'p' has static
or thread storage duration. It's perfectly usable as an initializer for
a variable with automatic storage duration.
> another similar surprising case is
>
> void *q = (void*)((char*)1 - (char*)1);
And only a problem if q has static or thread storage duration.
> the initializer is not an address constant, not a null pointer
> constant nor an address constant +- an integer constant
> expression.
Well, given the potential problems with (char*)1, I don't see that as a
big problem. Even if (char*)1 is defined by the implementation as having
a valid value, the subtraction is an expression with a type of ptrdiff_t
and a value of 0. It doesn't qualify as an integer constant expression,
and it's conversion to void* is therefore not required to produce a null
pointer constant. It's covered only by the same rule (6.3.2.3p1) that
covers (char*)1, and it therefore has all of the same problems as (char*)1.
> i wonder if my interpretation is right and if there is
> some rational behind this
The places where integer constant expressions are required are places
where they must be evaluated at compile time to generate the right code.
This imposes a bit of a strain on the implementor, who would otherwise
be allowed to delay evaluation of such expressions until run-time. The
rules for integer constant expressions were intended to keep them
simple, to make them easy to identify and evaluate.
--
James Kuyper