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

50,821

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).

Share:
50,821
badkya
Author by

badkya

Updated on July 10, 2020

Comments

  • badkya
    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
    badkya over 14 years
    Yes, this is not my code... I ended up casting the pointer to long and the code worked.. sort of...
  • bdonlan
    bdonlan over 13 years
    Actually, it's only likely to work well if it's in the first 2G - from 2~4G sign extension will get you :)
  • Wexxor
    Wexxor almost 12 years
    You 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
    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, an int 4 bytes. The int 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 to int. 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 to int loses information, and you can't go back to a pointer form the int afterwards.
  • Admin
    Admin over 11 years
    Casting from pointer to any data maybe useful in user-defined callbacks. When callback is responsible for interpreting data. pthread_create() is an expamle.
  • jogojapan
    jogojapan about 11 years
    I 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
    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
    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
    jogojapan about 11 years
    Sorry, 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
    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
    jogojapan about 11 years
    The original pointers will become invalid... what could that possibly be good for?
  • doptimusprime
    doptimusprime about 11 years
    For 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
    jogojapan about 11 years
    I 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
    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
    jkp almost 11 years
    I 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
    Jonathan Leffler almost 11 years
    If 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
    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
    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
    doptimusprime about 10 years
    The 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
    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
    Jacksonkr over 8 years
    (unsigned int) self -> (intptr_t) self worked for me