tags:

views:

232

answers:

8

in c and/or c++: is it safe to cast an int to void pointer and back to int again?

based on this question: http://stackoverflow.com/questions/3567905/c-is-it-safe-to-cast-pointer-to-int-and-later-back-to-pointer-again

A: 

Not necessarily. Depends on the size of a pointer. Why would you want to do this?

Oli Charlesworth
One reason I've seen - an API lets you pass it a void* generic value that it will pass back to you at some later time. It's nice to have the flexibility of using an int instead of a pointer. A better API will use a type guaranteed to handle both, but that wasn't common in older code.
Mark Ransom
@Mark: That was perhaps true in the old days, but nowadays a `uintptr_t` really suits better for such a feature. If the platform doesn't have `uintptr_t` (I don't know of any) then probably such unifying type simply doesn't exist, and one should be really careful, anyhow.
Jens Gustedt
+1  A: 

No. A void pointer is no different from any other pointer with respect to size. Hence it will run into exactly the same types of issues as other pointer types.

JaredPar
Actually, it is (in C++). It's guaranteed to be large enough to store any other pointer. Any cast from T* to void* and back to T* is guaranteed to result in the same T* you initially casted.
Noah Roberts
@Noah, take a look at the linked question. The OP is asking if it's safe to cast `int` to `void*` and back again. I'm stating that `void*` is not special and it will have exactly the same problems as casting `int` to `T*`.
JaredPar
Not my fault you didn't say what you meant.
Noah Roberts
I think I get your point, but I take issue with "A void pointer is no different from any other pointer with respect to size". `void*` *is* special.
jamesdlin
+1  A: 

It's implementation defined just like the last question and for the same reason. It's less likely to result in misbehavior but it's still implementation defined.

Noah Roberts
It's a subtly different question - this one asks int->void*->int, whereas the other question asks void*->int->void*.
Steven Schlansker
I noticed the difference. Thanks.
Noah Roberts
+9  A: 

In most modern-day commonplace machines, probably.

However, I'd bet that there is some obscure compiler or configuration (say, a 16-bit addressed machine that uses 32-bit integer arithmetic) where that is not the case.

A uintptr_t is guaranteed to hold both, though, so use that type if you want to.

Steven Schlansker
+1  A: 

No. There might be certain circumstances where it appears to work for a certain compiler&settings, and then two years later you spend weeks debugging that something changed and the conversion no longer works as expected.

If you just design your code in a way that doesn't need this sort of behavior (best case avoids use of such conversion at all, worst case use char[]) then you won't have to worry about obscure bugs in the future.

Mark B
+2  A: 

Why would you want to do this?

Reply for C, I don't know enough about C++ for that: No, behavior is not defined to cast an int to void*. First of all you should always use uintptr_t if you have it for such a thing. Using int is an abuse.

Then, C does not guarantee anything if your uintptr_t doesn't come from a valid address. It only guarantees the other way round. Don't do it.

Edit: Here is the relevant part of the C99 standard. As you can see all alarms can go off...

An integer may be converted to any pointer type. Except as previously specified, 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

The last is particularly embarrassing since this means that the pointer value that is such obtained can not be used anymore, until it is overwritten:

Certain object representations need not represent a value of the object type. If the stored value of an object has such a representation and is read by an lvalue expression that does not have character type, the behavior is undefined. ... Such a representation is called a trap representation.

Jens Gustedt
All that says is "an int can be converted to a pointer type, but don't try to dereference it". It says nothing about the safety of later converting it back into an integer. (Which as stated, is probably safe on most modern platforms, but you should still use uintptr_t.)
Joe
@Joe: yes it does. In particular it says that the result may be a trap representation, or in other words an invalid value for the type. Trying to access a variable (of any type) that contains a trap representation is undefined behavior. I'll edit this in, too.
Jens Gustedt
+2  A: 

Here is an example where converting a pointer to an integer may not result in the same pointer when converting the integer to a pointer.

Given an architecture which has 24 bit addresses and uses two 16-bit quantities to describe the location. Let one quantity be the SEGMENT and the other OFFSET. A location is designated by the notation SEGMENT:OFFSET.

The actual 24-bit (Physical) address is calculated by:

address = segment * 16 + offset.

Using this notation, there can be more than one SEGMENT:OFFSET pair that describe the same physical address.

When converting to an integer, a 32-bit (unsigned) quantity is used (to simplify internal calculations in the processor). The problem is how to convert the physical address into the same SEGMENT::OFFSET that was used in the creation of the physical address.

A generic equation for converting integer to pointer is:

offset = address & 0xFFFF; // Mask off high order bits, keep lower 16.
segment = address >> 16;   // Shift address right 16 bits, zero fill.

Although the physical address of this new segment and offset is equal to the physical address of the original SEGMENT:OFFSET, the segments and offsets are not guaranteed to be the same.

To optimize code, there are processor instructions that use relative addressing in a segment. These instructions may get messed up when the SEGMENT value changes due to conversion from a physical address.

In this scenario, converting from a pointer to an integer is possible. HOWEVER, converting from the integer to the pointer IS STRONGLY DISCOURAGED. Hard to debug errors could occur during run-time.

Bonus question: Can you name the actual architecture?

Thomas Matthews
8088/8086/80286
R..
A: 

If the range of your integers is fairly small, you could always do something like:

static const char dummy[MAXVAL];

and then use dummy+i as a way of encoding i as a pointer. This is 100% portable. If you only care that it's 99% portable, you could use (char *)0 + i.

R..