[ACCEPTED]-How should I handle "cast from ‘void*’ to ‘int’ loses precision" when compiling 32-bit code on 64-bit machine?-32bit-64bit

Accepted answer
Score: 50

The issue is that, in 32bits, an int (which 5 is a 32bit integer) will hold a pointer 4 value.

When you move to 64bit, you can no 3 longer store a pointer in an int - it isn't 2 large enough to hold a 64bit pointer. The 1 intptr_t type is designed for this.

Score: 23

Your code is broken. It won't become any 19 less broken by ignoring the warnings the 18 compiler gives you.

What do you think will happen 17 when you try to store a 64-bit wide pointer 16 into a 32-bit integer? Half your data will 15 get thrown away. I can't imagine many cases 14 where that is the correct thing to do, or 13 where it won't cause errors.

Fix your code. Or 12 stay on the 32-bit platform that the code 11 currently works on.

If your compiler defines 10 intptr_t or uintptr_t, use those, as they are integer types 9 guaranteed to be large enough to store a 8 pointer.

If those types are not available, size_t or 7 ptrdiff_t are also large enough to hold a pointer 6 on most (not all) platforms. Or use long (is typically 5 64-bit on 64-bit platforms on the GCC compiler) or 4 long long (a C99 types which most, but not all compilers, support 3 in C++), or some other implementation-defined 2 integral type that is at least 64 bits wide 1 on a 64-bit platform.

Score: 15

My guess is OP's situation is a void* is 10 being used as general storage for an int, where 9 the void* is larger than the int. So eg:

int i = 123;
void *v = (void*)i;    // 64bit void* being (ab)used to store 32bit value
[..]
int i2 = (int)v;       // we want our 32bits of the 64bit void* back

Compiler 8 doesn't like that last line.

I'm not going 7 to weigh in on whether it's right or wrong 6 to abuse a void* this way. If you really 5 want to fool the compiler, the following 4 technique seems to work, even with -Wall:

int i2 = *((int*)&v);

Here 3 it takes the address of v, converts the 2 address to a pointer of the datatype you 1 want, then follows the pointer.

Score: 11

It's an error for a reason: int is only half 8 as big as void* on your machine, so you can't 7 just store a void* in an int. You would loose half 6 of the pointer and when the program later 5 tries to get the pointer out of that int again, it 4 won't get anything useful.

Even if the compiler 3 wouldn't give an error the code most likely 2 wouldn't work. The code needs to be changed 1 and reviewed for 64bit compatibility.

Score: 7

Casting a pointer to an int is horrible 10 from a portability perspective. The size 9 of int is defined by the mix of compiler 8 and architecture. This is why the stdint.h 7 header was created, to allow you to explicitly 6 state the size of the type you're using 5 across many different platforms with many 4 different word sizes.

You'd be better off 3 casting to a uintptr_t or intptr_t (from 2 stdint.h, and choose the one that best matches 1 the signedness you need).

Score: 4

You can try to use intptr_t for best portability 2 instead of int where pointer casts are required, such 1 as callbacks.

Score: 4

You do not want to suppress these errors 10 because most likely, they are indicating 9 a problem with the code logic.

If you suppresses 8 the errors, this could even work for a while. While 7 the pointer points to an address in the 6 first 4 GB, the upper 32 bits will be 0 5 and you won't lose any data. But once you 4 get an address > 4GB, your code will start 3 'mysteriously' not working.

What you should 2 do is modify any int that can hold a pointer 1 to intptr_t.

Score: 2

You have to manually edit those files in 2 order to replace them with code that isn't 1 likely to be buggy and nonportable.

Score: 2

Suppressing the warnings are a bad idea, but 11 there may be a compiler flag to use 64-bit 10 ints, depending on your compiler and architecture, and 9 this is a safe way to fix the problem (assuming 8 of course that the code didn't also assume 7 ints are 32-bit). For gcc, the flag is -m64.

The 6 best answer is still to fix the code, I 5 suppose, but if it's legacy third-party 4 code and these warnings are rampant, I can't 3 see this refactoring as being a very efficient 2 use of your time. Definitely don't cast 1 pointers to ints in any of your new code, though.

Score: 2

As defined by the current C++ standard, there 24 is no integer type which is guaranteed to 23 hold a pointer. Some platforms will have 22 an intptr_t, but this is not a standard 21 feature of C++. Fundamentally, treating 20 the bits of a pointer as if they were an 19 integer is not a portable thing to do (although 18 it can be made to work on many platforms).

If 17 the reason for the cast is to make the pointer 16 opaque, then void* already achieves this, so 15 the code could use void* instead of int. A 14 typedef might make this a little nicer in 13 the code

typedef void * handle_t;

If the reason for the cast is to 12 do pointer arithmetic with byte granularity, then 11 the best way is probably to cast to a (char 10 const *) and do the math with that.

If the 9 reason for the cast is to achieve compatibility 8 with some existing library (perhaps an older 7 callback interface) which cannot be modified, then 6 I think you need to review the documentation 5 for that library. If the library is capable 4 of supporting the functionality that you 3 require (even on a 64-bit platform), then 2 its documentation may address the intended 1 solution.

Score: 1

I faced similar problem. I solved it in 8 the following way:

 #ifdef 64BIT
 typedef uint64_t tulong;
 #else
 typedef uint32_t tulong;
 #endif

 void * ptr = NULL; //Whatever you want to keep it.
 int  i;
 i = (int)(tulong)ptr;

I think, the problem is 7 of typecasting a pointer to a shorter data 6 type. But for a larger type to int, it works 5 fine.

I converted this problem from typecasting 4 a pointer to long to typecasting a 64-bit integer 3 to 32-bit integer and it worked fine. I 2 am still in the search of a compiler option 1 in GCC/Clang.

Score: 1

Sometimes it is sensible to want to split 3 up a 64-bit item into 2 32-bit items. This 2 is how you would do it:

Header file:

//You only need this if you haven't got a definition of UInt32 from somewhere else
typedef unsigned int UInt32;

//x, when cast, points to the lower 32 bits
#define LO_32(x) (*( (UInt32 *) &x))

//address like an array to get to the higher bits (which are in position 1)
#define HI_32(x) (*( ( (UInt32 *) &x) + 1)) 

Source 1 file:

//Wherever your pointer points to
void *ptr = PTR_LOCATION 

//32-bit UInt containing the upper bits
UInt32 upper_half = HI_32(ptr); 

//32-bit UInt containing the lower bits
UInt32 lower_half = LO_32(ptr); 

More Related questions