34

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?

jalf
  • 229,000
  • 47
  • 328
  • 537
badkya
  • 677
  • 2
  • 8
  • 13
  • 29
    *"Is there a compiler flag to suppress these errors?"* That should **not** be your first idea, ever. Warnings are there for you to *fix*, not suppress. Waving this warning away could have fatal behavior as your addresses get chopped in half. You need to fix the use of the pointers. – GManNickG Jan 08 '10 at 01:41
  • Yes but "fatal behavior" is very easy to notice... – Inverse Jan 08 '10 at 01:58
  • 8
    Not as easy as compiler warnings. – Steve Jessop Jan 08 '10 at 02:08
  • 3
    "Fatal behavior" might not be noticeable until you've already used 4 GB of memory. – bk1e Jan 08 '10 at 02:31
  • @bk1e: Depends on your platform. On linux 64bit, all pointers will use the full 8 bytes, so it'd be noticeable right away. – pavpanchekha Jan 08 '10 at 03:19
  • Same on Windows. This is by design. – Hans Passant Jan 08 '10 at 06:07
  • What do you mean by storing object types? – GManNickG Jan 09 '10 at 22:45
  • 1
    Sorry I meant storing objects with different types... void* allows reusing the same pointer for multiple object types.. – badkya Jan 10 '10 at 18:39
  • So it is indeed and address, which means you can't just cast it other types. You've done what the lowest voted answer said. Use `intptr_t` instead. – GManNickG Jan 10 '10 at 20:15
  • 1
    I disagree with the one-sidedness of all these answers. I agree that if `void*` is a pointer to address space, threads or similar, these errors should be fixed. However, there are also instances where one knows that the conversion works, e.g. if one first converts from an `int` to a `void*`, and then wants to convert back. This is entirely portable (see the documentation on `reinterpret_cast`). The answers here so far still lack adequate support for such a situation. `int i2 = *((int*)&v);` as stated by erco is a start, but is that the optimal solution? – Cookie Jun 28 '12 at 10:34

12 Answers12

50

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.

Destroyica
  • 4,007
  • 2
  • 30
  • 48
Reed Copsey
  • 522,342
  • 70
  • 1,092
  • 1,340
23

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.

jalf
  • 229,000
  • 47
  • 328
  • 537
  • 3
    +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. – kcstrom May 15 '13 at 14:18
15

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.

erco
  • 151
  • 1
  • 2
  • 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! – jkp May 16 '13 at 13:03
11

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.

sth
  • 200,334
  • 49
  • 262
  • 354
  • 9
    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. – Wexxor Jul 13 '12 at 22:24
  • 2
    @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. – sth Jul 14 '12 at 01:27
7

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

Matthew Iselin
  • 9,728
  • 3
  • 47
  • 59
  • 1
    Casting from pointer to any data maybe useful in user-defined callbacks. When callback is responsible for interpreting data. pthread_create() is an expamle. –  Jan 12 '13 at 08:50
4

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

LiraNuna
  • 57,153
  • 14
  • 112
  • 136
4

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

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

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

R Samuel Klatchko
  • 70,693
  • 15
  • 126
  • 182
  • Actually, it's only likely to work well if it's in the first 2G - from 2~4G sign extension will get you :) – bdonlan Sep 24 '10 at 10:24
2

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

silentbicycle
  • 1,134
  • 6
  • 9
2

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

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

user168715
  • 5,201
  • 1
  • 29
  • 40
  • Yes, this is not my code... I ended up casting the pointer to long and the code worked.. sort of... – badkya Jan 08 '10 at 22:29
2

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

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

typedef void * handle_t;

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

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

Brent Bradburn
  • 40,766
  • 12
  • 126
  • 136
1

I faced similar problem. I solved it in 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 of typecasting a pointer to a shorter data type. But for a larger type to int, it works fine.

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

Jonathan Leffler
  • 666,971
  • 126
  • 813
  • 1,185
doptimusprime
  • 8,303
  • 5
  • 41
  • 82
  • 1
    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. – jogojapan Mar 04 '13 at 11:03
  • @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 Mar 05 '13 at 10:59
  • @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? – doptimusprime Mar 12 '13 at 03:59
  • 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. – jogojapan Mar 12 '13 at 04:06
  • @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. – doptimusprime Mar 12 '13 at 05:55
  • The original pointers will become invalid... what could that possibly be good for? – jogojapan Mar 12 '13 at 06:13
  • 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. – doptimusprime Mar 12 '13 at 06:25
  • 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. – jogojapan Mar 12 '13 at 06:33
  • 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. – Jonathan Leffler Jun 18 '13 at 06:16
  • @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)? – supercat Feb 10 '14 at 19:55
  • @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 Feb 11 '14 at 03:58
  • 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. – doptimusprime Feb 11 '14 at 03:59
  • @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. – supercat Feb 11 '14 at 06:38
1

Sometimes it is sensible to want to split up a 64-bit item into 2 32-bit items. This 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 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); 
RLLBcheese
  • 11
  • 1