How should I handle "cast from ‘void*’ to ‘int’ loses precision" when compiling 32-bit code on 64-bit machine?
Solution 1
The issue is that, in 32bits, an int (which is a 32bit integer) will hold a pointer value.
When you move to 64bit, you can no longer store a pointer in an int - it isn't large enough to hold a 64bit pointer. The intptr_t type is designed for this.
Solution 2
Your code is broken. It won't become any less broken by ignoring the warnings the compiler gives you.
What do you think will happen when you try to store a 64-bit wide pointer into a 32-bit integer? Half your data will get thrown away. I can't imagine many cases where that is the correct thing to do, or where it won't cause errors.
Fix your code. Or stay on the 32-bit platform that the code currently works on.
If your compiler defines intptr_t
or uintptr_t
, use those, as they are integer types guaranteed to be large enough to store a pointer.
If those types are not available, size_t
or ptrdiff_t
are also large enough to hold a pointer on most (not all) platforms. Or use long
(is typically 64-bit on 64-bit platforms on the GCC compiler) or long long
(a C99 types which most, but not all compilers, support in C++), or some other implementation-defined integral type that is at least 64 bits wide on a 64-bit platform.
Solution 3
My guess is OP's situation is a void* is being used as general storage for an int, where 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 doesn't like that last line.
I'm not going to weigh in on whether it's right or wrong to abuse a void* this way. If you really want to fool the compiler, the following technique seems to work, even with -Wall:
int i2 = *((int*)&v);
Here it takes the address of v, converts the address to a pointer of the datatype you want, then follows the pointer.
Solution 4
It's an error for a reason: int
is only half as big as void*
on your machine, so you can't just store a void*
in an int
. You would loose half of the pointer and when the program later tries to get the pointer out of that int
again, it won't get anything useful.
Even if the compiler wouldn't give an error the code most likely wouldn't work. The code needs to be changed and reviewed for 64bit compatibility.
Solution 5
Casting a pointer to an int is horrible from a portability perspective. The size of int is defined by the mix of compiler and architecture. This is why the stdint.h header was created, to allow you to explicitly state the size of the type you're using across many different platforms with many different word sizes.
You'd be better off casting to a uintptr_t or intptr_t (from stdint.h, and choose the one that best matches the signedness you need).
badkya
Updated on July 10, 2020Comments
-
badkya almost 4 years
I have a package that compiles and works fine on a 32-bit machine. I am now trying to get it to compile on a 64-bit machine and find the following error-
error: cast from ‘void*’ to ‘int’ loses precision
Is there a compiler flag to suppress these errors? or do I have to manually edit these files to avoid these casts?
-
badkya over 14 yearsYes, this is not my code... I ended up casting the pointer to long and the code worked.. sort of...
-
bdonlan over 13 yearsActually, it's only likely to work well if it's in the first 2G - from 2~4G sign extension will get you :)
-
Wexxor almost 12 yearsYou guys are all missing the point. He's down-casting, this is commonly used when you need to pass either a bunch of data via a pointer, or just an integer argument. Something else about the call, typically another argument, tells the function if the pointer argument is actually a pointer to some data, or just an integer that has been 'upcast' to a pointer. If the latter, then you need to 'downcast' that pointer back to an integer. Warnings are just that -- warnings. They're not god's law engraved in stone, and treating like they are isn't helping.
-
sth almost 12 years@Wexxor: No, the point is that the a pointer has a different size than an
int
, on his system. A pointer takes probably 8 bytes of space, anint
4 bytes. Theint
doesn't have enough space to store all the data in the pointer, and because of that you lose information if you convert the pointer toint
. You can't get these four bytes that you lost back afterwards by casting in the other direction. So in this case, casting the pointer toint
loses information, and you can't go back to a pointer form theint
afterwards. -
Admin over 11 yearsCasting from pointer to any data maybe useful in user-defined callbacks. When callback is responsible for interpreting data. pthread_create() is an expamle.
-
jogojapan about 11 yearsI am sorry, but this is not a valid solution. You first convert to an integer type large enough to hold the pointer, and then you narrow it anyway. This means you will lose information when you perform the cast on a 64 bit system. As soon as you cast that integer back to pointer, the address may be invalid, and dereferencing it will cause undefined behaviour.
-
doptimusprime about 11 years@jogojapan: You are right. But there be some circumstances where one can safely downcast pointer (e.g. difference between two pointers). I provide the solution if there is a dire need to cast pointer into integer. I know that is unsafe and one must consider his/her logic before thinking of it.
-
doptimusprime about 11 years@jogojapan: There are few valid cases where this is requirement. Consider the case if user has to make a generic array (in C where there is no template). Then in such case, user can use void pointers to store any object. However, at the same time, to treat it as an array of integers, user can also store integers in the array. Insertion will be allowed, but accessing or searching may be problematic due to compilation error. This is the valid case where this is required. Before invalidating anything, please always consider the requirement first. Also, think why it is required?
-
jogojapan about 11 yearsSorry, I had overlooked your previous reply to my comment. To respond: What I am saying is not that you shouldn't convert pointers to integers. That's perfectly fine. What I am saying is that you shouldn't convert 64-bit pointers to 32-bit integers. The OP tried this directly, and your answer still does it, except that it converts to 64-bit integer first, and then to 32-bit integer. But that doesn't change the fact that this narrowing causes information loss. This will trigger undefined behavior as soon as convert back to pointer and make use of the address.
-
doptimusprime about 11 years@jogjopan: What is said by you is perfectly right. That will definitely cause the loss of information. I would like to say that there may be some cases where this is required.
-
jogojapan about 11 yearsThe original pointers will become invalid... what could that possibly be good for?
-
doptimusprime about 11 yearsFor this, consider following case: You stored 32-bit integers in a generic array (which you implement using the array of void *). Now, you can easily store them in pointers. Later, you want to retrieve them. Then downcasting to 32-bit should not affect it. In this case, these pointers are 32-bit numbers. Now you will question why such array? This is generic array which is to store any item in this. User can use same array for different purpose.
-
jogojapan about 11 yearsI don't understand what that is good for. If you have 32-bit integers (used for some generic purpose), why would you want to store them in pointers? That does not make them more generic than they are already. If you can store "any item" in the pointer array, then you can store "any item" directly in the 32-bit integer array.
-
kcstrom almost 11 years+1 for referencing intptr_t. It is a valid use case to cast things from void* to integer types if you have a generic function that takes (void *) because the a user can pass their own data which may be a pointer or an integer into it. When that user data then gets passed back to the specific code making use of it, it can cast it back to an integer.
-
jkp almost 11 yearsI just came across some code doing the same thing - yeah, its not great but needed a solution to get it compiling on 64 bit machines - thanks for the heads-up!
-
Jonathan Leffler almost 11 yearsIf you want to ignore the problems until they hurt your customers, you can use GCC 4.7's improved error messaging to realize that the warning is given by
-Wpointer-to-int-cast
and can therefore be suppressed by-Wno-pointer-to-int-cast
. OTOH, I don't want to be one of your customers if you do that. -
supercat about 10 years@dbasic: The construct
int i=23; void *p = (void*)i;
will be Undefined Behavior unless, by chance, the value 23 was returned by some earlier pointer-to-integer cast. Is there any basis for ever expecting a pointer-to-integer cast to yield anything other than an abstract identifier for the pointer (which would be totally meaningless if any bits were lost)? -
doptimusprime about 10 years@supercat: There is only one basis which I think is that when you cast and integer to pointer first and then retrieve integer by pointer to integer cast. If you have an array of pointers, but you store integer in it (although inefficient way).
-
doptimusprime about 10 yearsThe construct
int i=23; void *p = (void*)i;
, I do not think it is an undefined behaviour unless you want to dereference p. It is integer to pointer cast. -
supercat about 10 years@dbasic: Not all representations allow arbitrary combinations of bits in a pointer. A compiler would be allowed to assign
p
to a register which can only be loaded with certain values and will not accurately hold any others. -
Jacksonkr over 8 years
(unsigned int) self
->(intptr_t) self
worked for me