A few things that puzzle me...

T

Tomás

Okay firstly I'll start with pointers. I have always thought of a pointer as
storage which holds a memory address, plain and simple. Let's say that on a
particular platform, an "int" is 32-Bit, and a memory address is 32-Bit.
Thus, if we have the following code:

int i = 7;

int* p = &i;

Then memory is allocated to store a 32-Bit "int", and the value "7" is
stored in it. Let's say that the address of this memory is 0x89ABCDEF. Now,
we store the address of "i" in the variable "p". So "p"'s value will be
0x89ABCDEF. They'll look like so in memory:

i: 0000 0000 0000 0000 0000 0000 0000 0111

p: 1000 1001 1010 1011 1100 1101 1110 1111


One of the first delicate matters about pointers is that a null pointer
isn't necessarily represented as all bits zero, as follows:

0000 0000 0000 0000 0000 0000 0000 0000


But, depending on the platform, it could have any bit pattern, such as:

1000 0000 0000 0000 0000 0000 0000 0000

or:

1111 1111 1111 1111 1111 1111 1111 1111


When you write the following in your code:

p = 0;

Then you're storing the null pointer value in a pointer variable, which may
or may not be the same bit pattern as integral zero.

Moving on...

Because of my understanding of a pointer as just storing a memory address, I
fail to understand why the following code wouldn't be legal:

int a;

int* p1 = &a;

char* p2 = reinterpret_cast<char*>( p1 );

double* p3 = reinterpret_cast<double*>( p2 );

std::string* p4 = reinterpret_cast<std::string*>( p3 );

int* p5 = reinterpret_cast<int*>(p4);

*p5 = 3;


I've often heard that the above code might not do what I expect it to do.
This leads me to believe that not all pointer types are the same. So my
first question is:

A) How are different pointer types different? My understanding is that they
simply store a memory address, so I would imagine they would all be the
same.


Another question which comes to mind is:

B) Do all pointer types have the same null value? Or can different types
have different null values?


Is there any sort of in-depth guide which deals with all the fanciful
possibilites of pointers?

And finally, is the following code legal:

int (&array)[5] = *reinterpret_cast< int (*)[5] > ( new int[5] );

delete &array[0];


If we look at it from the viewpoint of a pointer as simply storing a memory
address, then I forsee no problems with the above code.

-Tomás
 
A

Alf P. Steinbach

* Tomás:
A) How are different pointer types different? My understanding is that they
simply store a memory address, so I would imagine they would all be the
same.

Depends much on the machine. C++ logical requirements only tell you
that an ordinary data pointer might be of smaller size than a pointer to
member function. Then again, a conforming compiler can have
sizeof(bool) equal 10 000, just to take a number, and likewise for
pointers; although unnatural, the standard places no limits on size.


Another question which comes to mind is:

B) Do all pointer types have the same null value? Or can different types
have different null values?

Not necessarily, and yes.

Is there any sort of in-depth guide which deals with all the fanciful
possibilites of pointers?

Not as far as I know. It's best to avoid the fanciful anyway.

And finally, is the following code legal:

int (&array)[5] = *reinterpret_cast< int (*)[5] > ( new int[5] );

delete &array[0];

new[] used to allocate, delete to deallocate, oops.

Apart from that the reinterpret_cast may or may not do what you expect
it to.

Instead you should do

std::vector<int> array( 5 );

Get it?

If we look at it from the viewpoint of a pointer as simply storing a memory
address, then I forsee no problems with the above code.

Uh oh...
 
J

Jakob Bieling

Tomás said:
int a;

int* p1 = &a;

char* p2 = reinterpret_cast<char*>( p1 );

double* p3 = reinterpret_cast<double*>( p2 );

std::string* p4 = reinterpret_cast<std::string*>( p3 );

int* p5 = reinterpret_cast<int*>(p4);

*p5 = 3;


I've often heard that the above code might not do what I expect it to
do. This leads me to believe that not all pointer types are the same.

The above is legal. One could argue about it being unspecified (and
thus being unportable) or not. See 5.2.10/7.
And finally, is the following code legal:

int (&array)[5] = *reinterpret_cast< int (*)[5] > ( new int[5] );

delete &array[0];

Despite the mismatching delete call, this is *not* undefined, but
unspecified. In other words, you are not going to be portable with this.

regards
 
G

Greg

Tomás said:
Because of my understanding of a pointer as just storing a memory address, I
fail to understand why the following code wouldn't be legal:

int a;

int* p1 = &a;

char* p2 = reinterpret_cast<char*>( p1 );

double* p3 = reinterpret_cast<double*>( p2 );

std::string* p4 = reinterpret_cast<std::string*>( p3 );

int* p5 = reinterpret_cast<int*>(p4);

*p5 = 3;

The code is legal but its behavior is undefined.
I've often heard that the above code might not do what I expect it to do.
This leads me to believe that not all pointer types are the same. So my
first question is:

A) How are different pointer types different? My understanding is that they
simply store a memory address, so I would imagine they would all be the
same.

No, all pointer types are not the same. Types usually have alignment
requirements - which means that an object of that type can reside only
at certain addresses. And the fact that different types may have
different alignment requirements means that casting from one type to
another and back again is not guaranteed to point to the same memory
location as the original. So the behavior of the above code is
undefined.
Another question which comes to mind is:

B) Do all pointer types have the same null value? Or can different types
have different null values?

Not in a way that a program would ever be able to tell.
And finally, is the following code legal:

int (&array)[5] = *reinterpret_cast< int (*)[5] > ( new int[5] );

delete &array[0];

Since the program allocates an array of five ints and deletes a pointer
to one int, the program is not correct.

Greg
 
T

Tomás

And finally, is the following code legal:

int (&array)[5] = *reinterpret_cast< int (*)[5] > ( new int[5] );

delete &array[0];


Should've written:

delete [] &array[0];
 
S

Scott McPhillips [MVP]

Tomás said:
A) How are different pointer types different? My understanding is that they
simply store a memory address, so I would imagine they would all be the
same.

They are all memory addresses, but they are not all the same. In C++
each one of them has a type. The type information (maintained by the
compiler) determines the size and type of what the pointer reads/writes.
The type information also determines the size of the address offset to
add when the pointer is "incremented."
 
M

Martin Vejnar

Tomás said:
[snipped unnessesary speech about OP's platform's pointer representation]

Because of my understanding of a pointer as just storing a memory address, I
fail to understand why the following code wouldn't be legal:

int a;

int* p1 = &a;

char* p2 = reinterpret_cast<char*>( p1 );

double* p3 = reinterpret_cast<double*>( p2 );

std::string* p4 = reinterpret_cast<std::string*>( p3 );

int* p5 = reinterpret_cast<int*>(p4);

*p5 = 3;

What you have failed to understand is the way the Standard deals with
pointers. There are very few guarantees about reinterpret_cast'ing a
pointer, even fewer are about pointer's bit representation.

[5.2.10/3]
The mapping performed by reinterpret_cast is implementation-defined.

You can make no assumptions about the actual value of 'p2'. In
particular (however illogical it may seem), you cannot portably use 'p2'
to access the first byte of 'a'. The only thing that you can reliably do
with 'p2' is convert it back to 'int *' (assuming that char's alignment
is less or equal to int's alignment):

[5.2.10/7]
A pointer to an object can be explicitly converted to a pointer to an
object of different type. Except that converting an rvalue of type
"pointer to T1" to the type "pointer to T2" (where T1 and T2 are object
types and where the alignment requirements of T2 are no stricter than
those of T1) and back to its original type yields the original pointer
value, the result of such a pointer conversion is unspecified.

Without implementaion-specific information, it is impossible to say
whether "p1 == p5". With your implementation, it may hold. With mine, it
might not.
I've often heard that the above code might not do what I expect it to do.
This leads me to believe that not all pointer types are the same. So my
first question is:

A) How are different pointer types different? My understanding is that they
simply store a memory address, so I would imagine they would all be the
same.

_Different_ pointer types are different, because they point to objects
of _different_ type. The fact that you're asking questions about bit
representation of pointers suggests that you are introducing
non-portable features into your program.
B) Do all pointer types have the same null value? Or can different types
have different null values?

Are you asking about bit representations again (see above)? Just don't.
(And no, there is no such guarantee. The Standard doesn't talk about
bits in this respect very often.)
Is there any sort of in-depth guide which deals with all the fanciful
possibilites of pointers?

The Standard should be the source to consider. It may be harder to read
though. Unfortunately, I don't know of any books that could be useful to
you. Mr. Bazarov is usualy the one throwing book suggestions about ;-)
And finally, is the following code legal:

int (&array)[5] = *reinterpret_cast< int (*)[5] > ( new int[5] );
delete &array[0];

I rewrote the code to make it more understandable:

int * p = new int[5];
int (*array)[5] = reinterpret_cast<int(*)[5]> (p);

See above; 'array' holds an implementation-defined value. You can use it
neither to access the array you allocated nor to delete it. You *can* say:

int * q = reinterpret_cast<int *> (array);
delete[] q;

I hope I made it clearer for you.
Martin
 
O

Old Wolf

Tomás said:
Okay firstly I'll start with pointers. I have always thought of a pointer
as storage which holds a memory address, plain and simple. Let's
say that on a particular platform, an "int" is 32-Bit, and a memory
address is 32-Bit.
Thus, if we have the following code:

int i = 7;

int* p = &i;

Then memory is allocated to store a 32-Bit "int", and the value "7" is
stored in it. Let's say that the address of this memory is 0x89ABCDEF
. Now, we store the address of "i" in the variable "p". So "p"'s value will
be 0x89ABCDEF. They'll look like so in memory:

i: 0000 0000 0000 0000 0000 0000 0000 0111

p: 1000 1001 1010 1011 1100 1101 1110 1111

Well, it might. The system could store the four bytes
0xEF, 0xCD, 0xAB, 0x89. Or it could store every byte twice
and use 64 bits of storage (although that would be inefficient).
In short, the address can be stored in any way that is
convenient for the compiler, as long as the address can be
successfully retrieved from the storage when it is needed.
Because of my understanding of a pointer as just storing a memory address, I
fail to understand why the following code wouldn't be legal:

int a;

int* p1 = &a;
char* p2 = reinterpret_cast<char*>( p1 );
double* p3 = reinterpret_cast<double*>( p2 );

You could have alignment trouble here. For example, some
systems could require that a 'double' only exist where its
address is a multiple of 8, but ints can exist anywhere with
an address of any multiple of 4. If this is the case, and
the address in p2 is not a multiple of 8, then you have caused
undefined behaviour and you're likely to get an error thrown
by the operating system here.

Going back to the topic of representation of pointer values;
a system with 8-byte alignment for doubles might choose
to right-shift the address of the double by 3 bits (which have
to be 0 anyway) before storing it. This has the advantage of
not allowing you to address 32Gb of doubles instead of
4Gb, with a 32-bit pointer.
std::string* p4 = reinterpret_cast<std::string*>( p3 );

Same alignment problem again here. Also, of course, if you
actually try and dereference this pointer you're very unlikely
to have a valid string object there.
int* p5 = reinterpret_cast<int*>(p4);
*p5 = 3;

This is oK
I've often heard that the above code might not do what I expect it to do.
This leads me to believe that not all pointer types are the same.

They may all have different sizes and representations (except for
void*, char* and unsigned char* which must all be the same).
A) How are different pointer types different? My understanding is that they
simply store a memory address, so I would imagine they would all be the
same.

See above. There's more to "A memory address" than "A 32 bit number".
For example, 16-bit segmented architectures, or systems where the
'memory' is a reel of magnetic tape (Unlikely but possible).
Another question which comes to mind is:

B) Do all pointer types have the same null value? Or can different types
have different null values?

There is only one null value. But the representations of null can all
be different.

And finally, is the following code legal:

int (&array)[5] = *reinterpret_cast< int (*)[5] > ( new int[5] );

An (int *) might not be correctly aligned for an int (*)[5] .
(In practice this probably would never happen).
 

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

Members online

No members online now.

Forum statistics

Threads
474,260
Messages
2,571,038
Members
48,768
Latest member
first4landlord

Latest Threads

Top