This question is almost a duplicate of some others I've found, but this specifically concerns POSIX, and a very common example in pthreads that I've encountered several times. I'm mostly concerned with the current state of affairs (i.e., C99 and POSIX.1-2008 or later), but any interesting historical information is of course interesting as well.
The question basically boils down to whether b will always take the same value as a in the following code:
long int a = /* some valid value */ void *ptr = (void *)a; long int b = (long int)ptr;
I am aware that this usually works, but the question is whether it is a proper thing to do (i.e., does the C99 and/or POSIX standards guarantee that it will work).
When it comes to C99 it seems it does not, we have 6.3.2.3:
5 An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.56)
6 Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type.
Even using intptr_t the standard seems to only guarantee that any valid void* can be converted to intptr_t and back again, but it does not guarantee that any intptr_t can be converted to void* and back again.
However it is still possible that the POSIX standard allows this.
I have no great desire to use a void* as a storage space for any variable (I find it pretty ugly even if POSIX should allow it), but I feel I have to ask because of the common example use of the pthreads_create function where the argument to start_routine is an integer, and it is passed in as void* and converted to int or long int in the start_routine function. For example this manpage has such an example (see link for full code):
//Last argument casts int to void * pthread_create(&tid[i], NULL, sleeping, (void *)SLEEP_TIME); /* ... */ void * sleeping(void *arg){ //Casting void * back to int int sleep_time = (int)arg; /* ... */ }
I've also seen a similar example in a textbook (An Introduction to Parallel Programming by Peter S. Pacheco). Considering that it seems to be a common example used by people who should know this stuff much better than me, I'm wondering if I'm wrong and this is actually a safe and portable thing to be doing.
You're return ing the value of int sum by setting a void * address to it. In this case, the address is not valid. But, if you keep that in mind and get the value of sum by casting a void * to int it will work.
Now, we want to assign the void pointer to integer pointer, in order to do this, we need to apply the cast operator, i.e., (int *) to the void pointer variable. This cast operator tells the compiler which type of value void pointer is holding.
As you say, C99 doesn't guarantee that any integer type may be converted to void*
and back again without loss of information. It does make a similar guarantee for intptr_t
and uintptr_t
defined in <stdint.h>
, but those types are optional. (The guarantee is that a void*
may be converted to {u,}intptr_t
and back without loss of information; there's no such guarantee for arbitrary integer values.)
POSIX doesn't appear to make any such guarantee either.
The POSIX description of <limits.h>
requires int
and unsigned int
to be at least 32 bits. This exceeds the C99 requirement that they be at least 16 bits. (Actually, the requirements are in terms of ranges, not sizes, but the effect is that int
and unsigned int
must be at least 32 (under POSIX) or 16 (under C99) bits, since C99 requires a binary representation.)
The POSIX description of <stdint.h>
says that intptr_t
and uintptr_t
must be at least 16 bits, the same requirement imposed by the C standard. Since void*
can be converted to intptr_t
and back again without loss of information, this implies that void*
may be as small as 16 bits. Combine that with the POSIX requirement that int
is at least 32 bits (and the POSIX and C requirement that long
is at least 32 bits), and it's possible that a void*
just isn't big enough to hold an int
or long
value without loss of information.
The POSIX description of pthread_create()
doesn't contradict this. It merely says that arg
(the void*
4th argument to pthread_create()
) is passed to start_routine()
. Presumably the intent is that arg
points to some data that start_routine()
can use. POSIX has no examples showing the usage of arg
.
You can see the POSIX standard here; you have to create a free account to access it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With