Cast to void * and back.

J

JC

I know that it is guaranteed that pointer types can be cast to a (void
*) and back to the same pointer type again without losing information;
is this also true of the integer types short, int, and long? E.g.:

long value = ...;
assert((long)(void *)value == value);

I imagine it's not necessarily true for large integers like long long,
or compiler-specific types like __int64. Is it at least true for int?

Thanks,
Jason
 
A

Andrey Tarasevich

JC said:
I know that it is guaranteed that pointer types can be cast to a (void
*) and back to the same pointer type again without losing information;
is this also true of the integer types short, int, and long? E.g.:

The language specification guarantees that the

pointer -> integral type -> pointer

round-trip conversion yields the original pointer value for any pointer
type, assuming that the integral type is "large enough" to hold the
intermediate value. But if I understood you correctly, you are talking
about a different round-trip conversion

integral -> void* -> integral

This one, I believe, is not guaranteed to yield the original pointer
value. There's nothing in the language specification that would
explicitly guarantee that. In theory, it is quite possible that even the
'void*' pointers on a given platform are supposed to obey certain
alignment requirements. On such a platform, an attempt to convert an
arbitrary integer to pointer type might result in the original value
getting transformed ("aligned"), thus resulting in a different value
after the conversion back to the integral type.

Of course, in practice, on a platform with "non-aligned" 'void*'
pointers you can expect this round-trip conversion to restore the
integral value, assuming that 'void*' is large enough to hold it.
 
J

jasonc

It's not just alignment; certain values in a void* could even cause a
trap. Using an integral type where one wants to handle both integer and
pointer types is superior to using a void*, though both are inferior to
simply using a union, of course.


All right; thanks!

Jason
 
J

Juha Nieminen

JC said:
I know that it is guaranteed that pointer types can be cast to a (void
*) and back to the same pointer type again without losing information;

Actually that's not necessarily the case with *all* pointer types.
More precisely, pointers to member functions of a class can be larger
than other types of pointers (eg. pointers to regular functions), and
consequently larger than void*.

Case in point:

//--------------------------------------------------------------------------
#include <iostream>

class A {};

int main()
{
void* vptr;
void (*fptr)();
void (A::*mptr)();

std::cout << sizeof(vptr) << " "
<< sizeof(fptr) << " "
<< sizeof(mptr) << "\n";
}
//--------------------------------------------------------------------------

In my machine that prints:

4 4 8
 
A

Alf P. Steinbach

* Juha Nieminen:
Actually that's not necessarily the case with *all* pointer types.
Right.


More precisely, pointers to member functions of a class can be larger
than other types of pointers (eg. pointers to regular functions), and
consequently larger than void*.

But this is not the counter-example. Member pointers are not "pointers" in this
regard. They're more like smart offsets, just employing the pointer *notation*
like iterators and smart pointers do, neither of which are necessarily pointers.

The single counter example is instead pointers to routines.

E.g. a 'void (*)()' can not be cast to 'void*' in standard C++, because in
standard C++ code and data live in two logically separate address spaces.


Cheers,

- Alf
 
J

Juha Nieminen

Alf said:
E.g. a 'void (*)()' can not be cast to 'void*' in standard C++, because
in standard C++ code and data live in two logically separate address
spaces.

Wouldn't that only be relevant if you are *comparing* pointers
(because if function pointers and data pointers use their independent
address spaces, then comparing the value of a function pointer to the
value of a data pointer might give you that they are equal, even though
they really aren't; they are equal in value, but are pointing to
completely different locations)?

As long as a function pointer can be converted to an integral, I don't
see the problem. (Or does the C++ standard not guarantee that function
pointers can be converted to integrals?)
 
A

Alf P. Steinbach

* Juha Nieminen:
Wouldn't that only be relevant if you are *comparing* pointers
(because if function pointers and data pointers use their independent
address spaces, then comparing the value of a function pointer to the
value of a data pointer might give you that they are equal, even though
they really aren't; they are equal in value, but are pointing to
completely different locations)?

No, it's very strict: function pointers and data pointers are two different
kinds of beasts, and in standard C++ never shall the two twains intermingle.

As long as a function pointer can be converted to an integral, I don't
see the problem. (Or does the C++ standard not guarantee that function
pointers can be converted to integrals?)

Now you're asking a difficult question! :)

Have to look that up...

OK, you can do that, for an integral type of sufficient size. And you can
convert a routine pointer to a pointer to routine of different type, and you can
convert an object pointer to a pointer to object of different type (but no
cross-breeding). Except for restrictions on casting away constness.


Cheers & hth.,

- Alf
 
J

James Kanze

* Juha Nieminen:
But this is not the counter-example. Member pointers are not
"pointers" in this regard. They're more like smart offsets,
just employing the pointer *notation* like iterators and smart
pointers do, neither of which are necessarily pointers.

I'm not sure about the syntax business. Normal pointers don't
use the ->* or .* operators, and the * operator (nor any pointer
arithmetic) won't work with pointers to members. They're
definitely different beasts from regular pointers.

Like in some other cases, the standard isn't really consistent
here; sometimes, they're called pointers, but in other cases,
when the standard says pointer, they aren't included.
The single counter example is instead pointers to routines.
E.g. a 'void (*)()' can not be cast to 'void*' in standard
C++, because in standard C++ code and data live in two
logically separate address spaces.

I'm not sure what you mean by separate address spaces. What is
sure is that they can, and in some implementions do have
different representations: I've used systems where pointers to
functions are 32 bits, but pointers to data 16, and vice versa.

Posix adds the guarantee that on a Posix conformant system, the
to do have the same representation (although I've worked on
pre-Posix Unix where this wasn't the case). So while you can't
(yet---C++0x makes it "conditionally" legal) directly convert a
void* to a void (*)() (a compiler diagnostic is required in
pre-0x C++), you can do things like:

void (*pf)() ;
*reinterpret_cast< void** >( &pf ) = dlsym( ... ) ;

.. (See dlsym in the Posix standard.)

Note that Windows has the opposite problem---if your dll
contains data, you have to use something like:

void* p ;
*reinterpret_cast< FARPTR* >( &p ) == GetProcAddress(...) ;

or something similar. (I can't find anything that guarantees
that this must work, but in practice, I think it's safe.)
 
J

James Kanze

* Juha Nieminen:
No, it's very strict: function pointers and data pointers are
two different kinds of beasts, and in standard C++ never shall
the two twains intermingle.

This will change in C++0x. Conditionally:).

I'm not sure what "conditionally supported" means in C++0x. I
think it's basically an "implementation defined" in which one of
the allowed possibilities is an error from the compiler. (And
if there isn't a diagnostic, the code is required to work.)
Now you're asking a difficult question! :)
Have to look that up...
OK, you can do that, for an integral type of sufficient size.
And you can convert a routine pointer to a pointer to routine
of different type, and you can convert an object pointer to a
pointer to object of different type (but no cross-breeding).
Except for restrictions on casting away constness.

The real point is that the integral type of sufficient size may
not be the same for the two types of pointers. But you're
right that even if it is the same, there's no guarantee that the
respresentations of the two are in any way compatible, or
exclusive---it would be perfectly legal of an int resulting from
the conversion of a void* compare equal to one resulting from
the conversion of a void (*)(), even though they refer to
completely different memory cells. (A good example of this
would be small model on an Intel 8086: both types of pointers
are 16 bits, but when used, they will use different segment
registers, and access completely different physical addresses.)
 
J

jasonc

This will change in C++0x.  Conditionally:).

I'm not sure what "conditionally supported" means in C++0x.  I
think it's basically an "implementation defined" in which one of
the allowed possibilities is an error from the compiler.  (And
if there isn't a diagnostic, the code is required to work.)

.... really? Am I misunderstanding what you are saying about C++0x, or
does this seem like a portability nightmare waiting to happen?


Jason
 
J

James Kanze

... really? Am I misunderstanding what you are saying
about C++0x, or does this seem like a portability
nightmare waiting to happen?

I'd say that it's a portability nightmare which has already
happened. I regularly have to modify my sources when I
encounter a new implementation of the library, because I've
forgotten to include a header for something I needed, but it
was included indirectly from another header.

(In this respect, I try to compile all my code using Sun CC
with the STLPort. The quality of the library itself is so
bad you really can't use it in production code, but it does
seem to indirectly include a lot less than any of the other
implementations I've used.)
 

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

Forum statistics

Threads
473,996
Messages
2,570,238
Members
46,826
Latest member
robinsontor

Latest Threads

Top