Re: Just how powerful is the cast?
Frederick Gotham wrote:
I'd like to know what happens however when we explicitly use a cast:
p = reinterpret_cast<char*>(0);
p = (char*)0;
But note that the cast in the second line here is special. It creates a
null pointer constant of type char *. The zero is an integral constant
expression having value zero, and as such it is a null pointer
constant. It can be converted to a null pointer value of a specific
pointer type by means of a static_cast or equivalent. Note that I said
value, not null pointer constant. The expression 0 is a null pointer
constant. The expression (char *) 0 is a null pointer value of type
char *.
On the other hand, the result of the reinterpret_cast is similar to
this:
int non_constant_zero = 0;
p = (char *) non_constant_zero;
I.e. implementation-defined conversion. It could well be that it will
just convert the 0 to an all-zero bit pattern. This would be the the
most sensible way to do it.
The reason for this is that the C++ standard does not require
reinterpret_cast to treat null pointer constants specially, and fall
back on the portable conversion.
Do the above two lines of code set our pointer to the legitimate null
pointer value, or do they set it to all bits zero? (My guess is that it's
the null pointer value)
Or consider the following:
#include <iostream>
int main()
{
unsigned i;
std::cin >> i;
/* Let's assume that user types in 0 */
char *p = reinterpret_cast<char*>( i );
Here, you can also write
char *p = (char *) i;
This is semantically equivalent to reinterpret_cast. The C style cast
in C++ is just an interface to the C++-style casts.
The only one of the C++ style casts (const_cast, static_cast,
dynamic_cast, reinterpret_cast) which can do an int to char *
conversion is reinterpret_cast. So that is the one that is will be
chosen to implement the C style cast notation.
The literal, 0, is of the type "signed int". I've always thought it
strange that the following triggers a type-mismatch error:
char *p = 5;
While the following doesn't:
char *p = 0;
This idea is inherited from the ANSI C language. An integral constant
expression that has value zero plays a special semantic role in the
language. It serves as a null pointer constant. In ANSI C, in fact,
even such a constant cast to (void *) is still a null pointer constant,
and not a null pointer value of type void *. So for instance, this is
valid:
void (*f)(int) = (void *) 0;
whereas non-constant (void *) values cannot be converted to function
pointers. The entire "(void *) 0" is a special expression that just
means "null pointer constant". (In the C language, I repeat! In C++
(void *) 0 is a null pointer value of type void *, and not a null
pointer constant.
From the knowledge I have at the moment, it seems to me that an
expression known at compile-time to be equal to zero gets special
treatment -- even when cast explicitly.
That is correct. When cast explicitly, its special semantic role is
taken into account, depending on the cast operator! reinterpret_cast
doesn't care; it treats the null pointer constant as an integer zero.
This would lead me to believe
that the following line sets p to the null pointer value:
char *p = reinterpret_cast<char*>( 7 - 5 - 2 );
But here, you specifically request the non-portable conversion of
reinterpret_cast, so its special semantics apply. Under that semantics,
the zero is really just an ordinary zero. The reinterpret_cast does not
handle zero constants specially, and it does not have "fall back"
behavior onto portable conversions.
A null pointer constant can be converted to char * by a weaker
conversion. So if you write
char *p = (char *) (7 - 5 - 2);
This C-style cast is /not/ the same as a reinterpret_cast. A
static_cast will do this job, so that's what it's equivalent to:
char *p = static_cast<char *>(7 - 5 - 2);
And because a cast is not needed at all to convert 7 - 5 - 2 to char
*, this static_cast doesn't do anything special. It does the same
conversion that happens when some variable of type char * is
initialized with a null pointer constant, e.g:
typedef char *T;
T temp(7-5-2); // no cast needed
Note finally that a reinterpret_cast /is/ required to convert null
pointer /values/ from one type to null pointer values of another. So
this should in fact give you a null pointer:
char *p = reinterpret_cast<char *>((void *) (7 - 5 - 2));
The reuqirement comes from paragraph 8 of section 5.2.10. :)