79

I have a function with prototype void* myFcn(void* arg) which is used as the starting point for a pthread. I need to convert the argument to an int for later use:

int x = (int)arg;

The compiler (GCC version 4.2.4) returns the error:

file.cpp:233: error: cast from 'void*' to 'int' loses precision

What is the proper way to cast this?

sbi
  • 219,715
  • 46
  • 258
  • 445
Joshua D. Boyd
  • 4,808
  • 3
  • 29
  • 44
  • 10
    Are you on a 64-bit system? – Artelius Oct 28 '09 at 22:09
  • 8
    You should be doing int x = *((int *)arg); You are casting from void * to int that is why you get the warning – Cratylus Nov 05 '10 at 18:51
  • 14
    I cannot reverse my upvote of user384706's answer, but it's wrong. `x = *((int *)arg);` is how you'd get an `int` /at the location pointed to by/ the `void*`. It does not reinterpret the `void*` itself as an `int`. – JDonner Mar 18 '12 at 21:16
  • int *x = (int *)arg; // the value is *x – c0ming Feb 26 '14 at 02:06

17 Answers17

77

You can cast it to an intptr_t type. It's an int type guaranteed to be big enough to contain a pointer. Use #include <cstdint> to define it.

Ferruccio
  • 98,941
  • 38
  • 226
  • 299
38

Again, all of the answers above missed the point badly. The OP wanted to convert a pointer value to a int value, instead, most the answers, one way or the other, tried to wrongly convert the content of arg points to to a int value. And, most of these will not even work on gcc4.

The correct answer is, if one does not mind losing data precision,

int x = *((int*)(&arg));

This works on GCC4.

The best way is, if one can, do not do such casting, instead, if the same memory address has to be shared for pointer and int (e.g. for saving RAM), use union, and make sure, if the mem address is treated as an int only if you know it was last set as an int.

onlooker
  • 573
  • 4
  • 3
  • 5
    This method will not work on 64 bit Big Endian platform, so it unnecessarily breaks portability. – dkz Aug 11 '14 at 17:08
  • 13
    This is not even remotely "the correct answer". This is not a conversion at all. This is memory reinterpretation - a completely unacceptable way to do what the OP is trying to do. – AnT stands with Russia Aug 20 '14 at 06:48
  • 4
    This is flat out wrong. No idea how it amassed 27 upvotes?! -1 – Lightness Races in Orbit Jul 29 '15 at 19:44
  • 7
    Uggh. Terrible solution. Wrong. This returns the first 32 bits of the pointer which may be the top or the bottom depending on big versus little endian, as comment #2 said. In the best case this will give the same results as the original code, with no advantages, but in the worst case it will fail in multiple catastrophic ways (type punning, endianness, less efficient, etc.) – Bruce Dawson Nov 06 '15 at 21:20
  • @onlooker, this is simply a cast mechanism. It is not at all a right answer. – siddhusingh Apr 29 '17 at 18:31
  • 1
    For those who are interested. I personally upvoted this answer because by it's first line of text it helped me to understand the reason of this strange error message and what am I, poor idiot, doing :D – Alex Aug 22 '18 at 01:21
15

Instead of:

int x = (int)arg;

use:

int x = (long)arg;

On most platforms pointers and longs are the same size, but ints and pointers often are not the same size on 64bit platforms. If you convert (void*) to (long) no precision is lost, then by assigning the (long) to an (int), it properly truncates the number to fit.

Yamaneko
  • 3,433
  • 2
  • 38
  • 57
Joshua D. Boyd
  • 4,808
  • 3
  • 29
  • 44
  • 14
    Not valid on Windows 64 - long is still 32-bit but pointers are 64-bit. – Jonathan Leffler Oct 28 '09 at 22:13
  • That's not a good idea if the original object (that was cast to void*) was an integer. If the sizes are different then endianess comes into play. – Martin York Oct 28 '09 at 22:19
  • 2
    @Martin York: No, it doesn't depend on endiannness. The mapping in pointer<->integer casts is implementation defined, but the intent was that if the pointer type is large enough and isn't forcefully aligned (`void*` doesn't) then round-trip cast integer-to-pointer-to-integer should produce the original value. Same for pointer-to-integer-to-pointer round trip. – AnT stands with Russia Oct 28 '09 at 22:43
13

There's no proper way to cast this to int in general case. C99 standard library provides intptr_t and uintptr_t typedefs, which are supposed to be used whenever the need to perform such a cast comes about. If your standard library (even if it is not C99) happens to provide these types - use them. If not, check the pointer size on your platform, define these typedefs accordingly yourself and use them.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
8

Casting a pointer to void* and back is valid use of reinterpret_cast<>. So you could do this:

pthread_create(&thread, NULL, myFcn, new int(5)); // implicit cast to void* from int*

Then in myFcn:

void* myFcn(void* arg)
{
    int*  data = reinterpret_cast<int*>(arg);
    int   x    = *data;
    delete data;

Note: As sbi points out this would require a change on the OP call to create the thread.

What I am trying to emphasis that conversion from int to pointer and back again can be frough with problems as you move from platform to platform. BUT converting a pointer to void* and back again is well supported (everywhere).

Thus as a result it may be less error prone to generate a pointer dynamcially and use that. Remembering to delete the pointer after use so that we don't leak.

Martin York
  • 257,169
  • 86
  • 333
  • 562
  • 1
    But that's different. From the question I presume the OP does `pthread_create(&thread, NULL, myFcn, 5)`, which will make your idea fail. – sbi Oct 28 '09 at 22:30
  • 2
    What I am saying is that it would be safer to use new(5) rather than 5 and deal with it appropriately at the other end. – Martin York Oct 28 '09 at 22:33
  • 1
    I understood, but that would introduce dynamic memory and ugly lifetime issues (an object allocated by one thread must be freed by some other) - all just to pass an `int` to a thread. – sbi Oct 28 '09 at 22:35
  • Even if there are issues with freeing the int in a thread (which there usually is not). Then loosing one int to memory management seems preferable to getting the wrong int at the other end. – Martin York Oct 28 '09 at 22:38
  • 1
    Ugh. I strongly disagree. And casting an `int` to `void*` and back can only be a problem on a platform where `sizeof(int)>sizeof(void*)`. I'm not sure the standard even allows such platforms. – sbi Oct 28 '09 at 22:43
  • 3
    There is absolutely not gurantee that sizeof(int) <= sizeof(void*). Infact I know several systems where that does not hold. – Martin York Oct 28 '09 at 22:45
  • 1
    @Martin: Thanks for setting me straight on what the standard does (not) require regarding the size of built-ins. But which are those platforms? And do they implement Posix threads anyway? – sbi Oct 28 '09 at 22:51
  • Off the top of my head: KSR-64. Which supported 64 bit address space and 128 bit integers. (PS the 64 in KSR-64 is the number of processors not the address size :-) It ran a standard version of Unix (forget which veriant) that include pthread support. A lot of compilers also support using larger integers with just an extra flag. – Martin York Oct 28 '09 at 22:58
  • @Martin: That's one, and it's one even google hasn't heard about. (Note that the C++ code written by me runs on half a dozen of different platforms - and that's only because I consider all Linux versions as a single platform -, so I'm not as Wintel-centric as many others.) Please be reasonable. There might even be platforms where you are not allowed to cast between `int*` and `void*`. – sbi Oct 28 '09 at 23:17
  • @sbi: Standard gurantees that casting any pointer to void* and back again will result in a valid value. As for KSR: http://en.wikipedia.org/wiki/Kendall_Square_Research Also even gcc can support 128 bit integers:http://gcc.gnu.org/ml/gcc/2000-09/msg00416.html – Martin York Oct 29 '09 at 00:32
  • 1
    @SBI: At my last company I was responcable for building ACE/TAO across 32 different platfrom configuration (Hardware/OS/Compiler) and for each configuration we had 8 different build types (Debug-release/Single-Multi Thread/Shared-Static lib) I did not have the luxary of assuming that anything would work unless it was explicitly stated in the standard and even then there are some gotchas. – Martin York Oct 29 '09 at 00:37
  • @sbi. Your method will work on most user desktops for the next 3-5 years or so. But will eventually fail. My method is guranteed to work by the standard. – Martin York Oct 29 '09 at 00:47
  • @sbi: even if casting int to void* works, imaging now having to pass 2 ints to a thread, or a string? are int's so special? (obviously, they're special enough for some to provide an intptr_t type) – stefaanv Oct 29 '09 at 09:47
7

Instead of using a long cast, you should cast to size_t.

int val= (int)((size_t)arg);
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
3

The proper way is to cast it to another pointer type. Converting a void* to an int is non-portable way that may work or may not! If you need to keep the returned address, just keep it as void*.

Khaled Alshaya
  • 94,250
  • 39
  • 176
  • 234
  • The point is (probably) that the value passed to the thread is an integer value, not really a 'void *'. – Jonathan Leffler Oct 28 '09 at 22:12
  • Thanks Jonathan, I was thinking about my answer in another thread: http://stackoverflow.com/questions/1593580/c-how-to-get-the-address-stored-in-a-void-pointer/1593588#1593588 – Khaled Alshaya Oct 28 '09 at 22:23
  • 2
    AraK is correct, passing integers a pointers are not necessarily interchangeable. He should pass the address of the integer, the thread should get that address, `static_cast` it to an `int*`, then get the value. This must be done before the integer goes out of scope. – GManNickG Oct 28 '09 at 22:32
3

Safest way :

static_cast<int>(reinterpret_cast<long>(void * your_variable));

long guarantees a pointer size on Linux on any machine. Windows has 32 bit long only on 64 bit as well. Therefore, you need to change it to long long instead of long in windows for 64 bits. So reinterpret_cast has casted it to long type and then static_cast safely casts long to int, if you are ready do truncte the data.

Yamaneko
  • 3,433
  • 2
  • 38
  • 57
siddhusingh
  • 1,832
  • 4
  • 25
  • 30
2

There is no "correct" way to store a 64-bit pointer in an 32-bit integer. The problem is not with casting, but with the target type loosing half of the pointer. The 32 remaining bits stored inside int are insufficient to reconstruct a pointer to the thread function. Most answers just try to extract 32 useless bits out of the argument.

As Ferruccio said, int must be replaced with intptr_t to make the program meaningful.

Community
  • 1
  • 1
MKaama
  • 1,732
  • 2
  • 19
  • 28
1

If you call your thread creation function like this

pthread_create(&thread, NULL, myFcn, reinterpret_cast<void*>(5));

then the void* arriving inside of myFcn has the value of the int you put into it. So you know you can cast it back like this

int myData = reinterpret_cast<int>(arg);

even though the compiler doesn't know you only ever pass myFcn to pthread_create in conjunction with an integer.

Edit:

As was pointed out by Martin, this presumes that sizeof(void*)>=sizeof(int). If your code has the chance to ever be ported to some platform where this doesn't hold, this won't work.

sbi
  • 219,715
  • 46
  • 258
  • 445
1

I would create a structure and pass that as void* to pthread_create

struct threadArg {
    int intData;
    long longData;
    etc...
};


threadArg thrArg;
thrArg.intData = 4;
...
pthread_create(&thread, NULL, myFcn, (void*)(threadArg*)&thrArg);


void* myFcn(void* arg)
{
    threadArg* pThrArg = (threadArg*)arg;
    int computeSomething = pThrArg->intData;
    ...
}

Keep in mind that thrArg should exist till the myFcn() uses it.

Akos Hamori
  • 151
  • 1
  • 5
0

Don't pass your int as a void*, pass a int* to your int, so you can cast the void* to an int* and copy the dereferenced pointer to your int.

int x = *static_cast<int*>(arg);
Yamaneko
  • 3,433
  • 2
  • 38
  • 57
stefaanv
  • 14,072
  • 2
  • 31
  • 53
  • If you do this, you have the thread reference a value that (hopefully still) lives in some other thread. That could create all kinds of trouble. – sbi Oct 28 '09 at 22:38
  • 1
    pthread passes the argument as a void*. This is an old C callback mechanism so you can't change that. And you can't pass a pointer to a stack based object from the other thread as it may no longer be valid. – Martin York Oct 28 '09 at 22:40
  • There are ways to prevent this: pass a dynamic allocated argument if your not the passing thread is not static or if your argument is a local variable, otherwise there is no issue. – stefaanv Oct 28 '09 at 22:42
  • I agree passing a dynamically allocated pointer is fine (and I think the best way). But you seem to suggest by your answer that the user can pass 5 to pthread_create and then perform the above cast to get it back. – Martin York Oct 29 '09 at 00:44
0

What you may want is

int x = reinterpret_cast<int>(arg);

This allows you to reinterpret the void * as an int.

Artelius
  • 48,337
  • 13
  • 89
  • 105
  • Note: This is only appropriate is you cast the `int` to a `void *` in the first place. – Artelius Oct 28 '09 at 22:30
  • @Artelius: Which, presumably, is exactly what Joshua did: `pthread_create(&thread, NULL, myFcn, reinterpret_cast(5));`. – sbi Oct 28 '09 at 22:37
  • A C++ reinterpret cast will not solve the problem. If the original type is a void *, converting to an int may lose date on platforms where sizeof(void *) != sizeof(int) (which is true of LP64 programming model). – R Samuel Klatchko Oct 28 '09 at 22:49
  • 2
    Hmm? As long as `sizeof(void *) > sizeof(int)`, there is no problem, seeing as you first cast from `int` to `void *` and then back again. – Artelius Oct 28 '09 at 23:04
0

In my case, I was using a 32-bit value that needed to be passed to an OpenGL function as a void * representing an offset into a buffer.

You cannot just cast the 32-bit variable to a pointer, because that pointer on a 64-bit machine is twice as long. Half your pointer will be garbage. You need to pass an actual pointer.

This will get you a pointer from a 32 bit offset:

int32 nOffset   = 762;       // random offset
char * pOffset  = NULL;      // pointer to hold offset
pOffset         += nOffset;  // this will now hold the value of 762 correctly
glVertexAttribPointer(binding, nStep, glType, glTrueFalse, VertSize(), pOffset);
Relio
  • 1
0

A function pointer is incompatible to void* (and any other non function pointer)

0
//new_fd is a int
pthread_create(&threads[threads_in_use] , NULL, accept_request, (void*)((long)new_fd));

//inside the method I then have
int client;
client = (long)new_fd;

Hope this helps

Pieces
  • 2,256
  • 5
  • 25
  • 39
-3

Well it does this because you are converting a 64 bits pointer to an 32 bits integer so you loose information.

You can use a 64 bits integer instead howerver I usually use a function with the right prototype and I cast the function type : eg.

void thread_func(int arg){
...
}

and I create the thread like this :

pthread_create(&tid, NULL, (void*(*)(void*))thread_func, (void*)arg);
Ben
  • 7,372
  • 8
  • 38
  • 46
  • 1
    If the function had the correct signature you would not need to cast it explicitly. – Martin York Oct 28 '09 at 22:29
  • But then you need to cast your arguments inside your thread function which is quite unsafe ... cf. this question – Ben Oct 28 '09 at 22:38
  • 2
    You think by casting it here that you are avoiding the problem! You are just making it bigger by tricking the compiler and the ABI. – Martin York Oct 28 '09 at 22:41
  • Casting arguments inside the function is a lot safer. What you do here is undefined behavior, and undefined behavior of very practical sort. – AnT stands with Russia Oct 28 '09 at 22:45
  • this way I convert an int to a void* which is much better than converting a void* to an int. I don't see how anything bad can happen . Please tell me ... – Ben Oct 28 '09 at 22:47
  • @Ben. You are not converting anything to anything. You are messing around with the function type not the parameter type. You are delibrately subverting the compilers ability to tell the real type of the function. It still passes a void* to your function no conversion is done it places the bit pattern of a void* on the stack. Your function treats the bit pattern on the stack as if it is an integer. – Martin York Oct 28 '09 at 22:54
  • @Ben: Your `thread_func` is called with a `void*`, which it interprets as an `int`. So you still convert (and how could you avoid it?), only it's done with function parameters on the stack. That's probably less safe than the explicit cast. Plus you cheated with function pointer types. Yuck. – sbi Oct 28 '09 at 22:54