Just how powerful is the cast?

F

Frederick Gotham

Let's assume that we're working on the following system:

CHAR_BIT == 8
sizeof( char* ) == 4 (i.e. 32-Bit)


Furthermore, lets assume that the memory addresses are distributed as
follows:

0x00000000 through 0xFFFFFFFE : Valid byte addresses
0xFFFFFFFF : Null pointer value

If we want to set a pointer to null, we can just do the following:

char *p = 0; /* Now contains 0xFFFFFFFF */

If, for some wacky reason, we wanted to set it to all bits zero, then we
could do:

memset(p, 0, sizeof p); /* Now contains 0x00000000 */

I'd like to know what happens however when we explicitly use a cast:

p = reinterpret_cast<char*>(0);

p = (char*)0;

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 );
}

I would presume that the above code snippet sets p to all bits zero in
the above, rather than the legitimate null pointer value.

I'm very familiar with the concept of "type mismatch"; for instance, I
realise that the following is broken:

double *p;
float k = 34.2;

p = k;

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;


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. 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 );

, althought my example which contained user input would set the pointer
to all bits zero rather than the legitimate null pointer value.
 
B

Bo Persson

Frederick Gotham said:
Let's assume that we're working on the following system:

CHAR_BIT == 8
sizeof( char* ) == 4 (i.e. 32-Bit)


Furthermore, lets assume that the memory addresses are distributed
as
follows:

0x00000000 through 0xFFFFFFFE : Valid byte addresses
0xFFFFFFFF : Null pointer value

If we want to set a pointer to null, we can just do the following:

char *p = 0; /* Now contains 0xFFFFFFFF */

If, for some wacky reason, we wanted to set it to all bits zero,
then we
could do:

memset(p, 0, sizeof p); /* Now contains 0x00000000 */

Assuming that char(0) is all zero bits, yes. Who knows on this whacky
system?

I'd like to know what happens however when we explicitly use a cast:

p = reinterpret_cast<char*>(0);

p = (char*)0;

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)

When p is a char*, the expressions

p = (char*)0;
and
p = 0;

are identical.

The reinterpret_cast is a bit fishy, because it is allowed to have
some implementation defined mappings. I believe we would have to
consult the (whacky) compiler manual.
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 );
}

I would presume that the above code snippet sets p to all bits zero
in
the above, rather than the legitimate null pointer value.

I wouldn't presume too much. :)
I'm very familiar with the concept of "type mismatch"; for instance,
I
realise that the following is broken:

double *p;
float k = 34.2;

p = k;

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;

That's just the way it is. Zero is special!
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. 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 );

If you skip the reinterpret_cast, you are right

char* p = 7 - 5 - 2;

creates a null pointer. With the cast, I don't know for sure.
, althought my example which contained user input would set the
pointer
to all bits zero rather than the legitimate null pointer value.

The user would surely input -5 instead, and break the code entirely.
:)


Bo Persson
 
R

Roberto Waltman

Bo Persson said:
"Frederick Gotham" skrev i meddelandet
...

Assuming that char(0) is all zero bits, yes. Who knows on this whacky
system?

No need to assume. That is 0, not '0';
 
B

Bo Persson

Roberto Waltman said:
No need to assume. That is 0, not '0';

But memset takes an int parameter, that it has to convert to type
char. On normal hardware this is easy, but on a hypothetical hardware
using 0xFFFFFFFF for the null pointer, who knows what bit pattern is
used for char(0)?!

Just trying to be picky! :)


Bo Persson
 
A

Andrey Tarasevich

Frederick said:
...
Furthermore, lets assume that the memory addresses are distributed as
follows:

0x00000000 through 0xFFFFFFFE : Valid byte addresses
0xFFFFFFFF : Null pointer value

If we want to set a pointer to null, we can just do the following:

char *p = 0; /* Now contains 0xFFFFFFFF */

If, for some wacky reason, we wanted to set it to all bits zero, then we
could do:

memset(p, 0, sizeof p); /* Now contains 0x00000000 */

I'd like to know what happens however when we explicitly use a cast:

p = reinterpret_cast<char*>(0);

p = (char*)0;

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)

Yes. Both produce the null pointer value. Note though that the second
one in this case is interpreted as 'static_cast', not as 'reinterpret_cast'.
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 );
}

I would presume that the above code snippet sets p to all bits zero in
the above, rather than the legitimate null pointer value.

You are right when you assume that the code is not guaranteed to produce
the null pointer value. However, the 'int -> char*' mapping produced by
'reinterpret_cast' in this case is implementation defined, which means
that in general case there's no guarantee that 'p' is all bits zero as well.
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;


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.

Yes, that's exactly correct. See the definition of 'null pointer
constant' in the language specification.
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 );

That's true. The same can be said about, for example,

char *p =
reinterpret_cast<char*>(sizeof(7)*2 - sizeof(5) - sizeof(int));

or

char *p = reinterpret_cast<char*>(false);
 
T

Thomas J. Gritzan

Bo said:
Assuming that char(0) is all zero bits, yes. Who knows on this whacky
system?

Assuming p still is = 0, this is undefined behaviour. Dereferencing a
null-pointer, isn't it?

Thomas
 
F

Frederick Gotham

Bo Persson posted:

But memset takes an int parameter, that it has to convert to type
char. On normal hardware this is easy, but on a hypothetical hardware
using 0xFFFFFFFF for the null pointer, who knows what bit pattern is
used for char(0)?!


The Standard says exactly how unsigned integers store their values (i.e.
i.e. something like "obey modulo X arithemtic").

It also says that the signed variety integer type has the same bit pattern
for the values which can be expressed accurately with the unsigned variety.

Therefore, the unsigned char variety of zero is all bits zero.

And hence, the signed char variety of zero is all bits zero.
 
K

Kaz Kylheku

Frederick said:
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. :)
 
A

Andrey Tarasevich

Kaz said:
...
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.

One the one hand, in the original version of the standard the normative
text does not require that null pointer constant is treated by
'reinterpret_cast' differently from other integral values. On the other
hand, the footnote 64 states that null pointer constants have to yield
null pointer values after being converted by 'reinterpret_cast'. It is
true that footnotes are not normative, but at least it demonstrates the
original intent of the committee. And the original intent was that
'reinterpret_cast<char*>(0)' does yield the null pointer value.

However, this will probably be changed in the future revisions of the
standard to the implementetion-defined behavior you describe (see
http://open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#463)
 
R

Rolf Magnus

Frederick said:
Bo Persson posted:




The Standard says exactly how unsigned integers store their values (i.e.
i.e. something like "obey modulo X arithemtic").

It also says that the signed variety integer type has the same bit pattern
for the values which can be expressed accurately with the unsigned
variety.

Therefore, the unsigned char variety of zero is all bits zero.

And hence, the signed char variety of zero is all bits zero.

Well, there could be different values for +0 and -0, so you have be careful
not to get a negative zero ;-)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,780
Messages
2,569,611
Members
45,270
Latest member
TopCryptoTwitterChannels_

Latest Threads

Top