31

I've been looking at Advanced Linux Programming by Mitchell, Oldham and Samuel. I've seen in the section on pthreads something about void pointers and casting that confuses me.

Passing an argument to pthread_create(), they don't cast the pointer to a void pointer even though that is what the function expects.

pthread_create( &thread, NULL, &compute_prime, &which_prime );

Here, which_prime is of type int.

But taking a value returned from the thread using pthread_join, they DO cast the variable to void pointer.

pthread_join( thread, (void*) &prime );

Here, prime is of type int again.

Why is casting done in the second instance and not in the first?

jww
  • 97,681
  • 90
  • 411
  • 885
Amoeba
  • 1,573
  • 4
  • 19
  • 25
  • 7
    `pthread_join`'s second argument is a void**. That code looks wrong. – John Zwinck Dec 09 '13 at 11:54
  • 1
    Looking at the full code (http://www.advancedlinuxprogramming.com/alp-folder/alp-ch04-threads.pdf) reveals, that the example misuses the `void*` returned by the thread function and received by `pthread_join()` as `int`. This is the relevant line of `compute_prime()`: `int candidate; ... return (void*) candidate;` So using `&prime` as 2nd argument to `int prime; ... pthread_join(..., &prime)` perfectly makes sense. However casr it to `void*` simply is wrong. If placeing a cast `void**` would have been valid, as by the declaration of `pthread_join(pthread_t, void **)`. – alk Jan 04 '14 at 13:53

5 Answers5

15

No need to cast from or to a pointer to void in C:

6.3.2.3 Pointers

1 A pointer to void may be converted to or from a pointer to any incomplete or object type. A pointer to any incomplete or object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.


The only exceptions from this are

  • when printing a pointer using the "%p" conversion specifier as it's only defined for void *.
  • when copying a pointer's value from an intptr_t or uintptr_t back to a void *.
bobbogo
  • 14,989
  • 3
  • 48
  • 57
alk
  • 69,737
  • 10
  • 105
  • 255
  • "when printing a pointer using the "%p" conversion specifier as it's only defined for void *" - I'm often printing non `void*` pointers using `%p` and it seems to work. Is it just not portable? Should I cast the pointers to `void*`? – Aviv Cohn Nov 24 '19 at 12:40
  • @AvivCohn: "*Should I cast the pointers*" To stay compatible, yes you should, else wise undefined behaviour is invoked, which might result in (seemingly) working or crash or .... – alk Nov 24 '19 at 13:14
12

The second example is a good example of why casting to void* is usually a mistake. It should be

void *primep = ′  // no cast needed
pthread_join(thread, &primep);

because pthread_join takes a void** as its second argument. The void* only makes sure the bug passes the compiler because the void* is converted to void** automatically.

So, when do you need to cast to void* or back:

  • when working with pointers stored as integers ((u)intptr_t);
  • when passing pointers to functions that have an incomplete prototype and take void* (or take a different type of pointer and you have void*); that usually means functions taking a variable number of arguments such as printf.
Fred Foo
  • 355,277
  • 75
  • 744
  • 836
9

In C, casting to void* from any pointer type and vice-versa is done implicitly. There's no need for the cast in the second example.

(Note that in C++ casting any pointer to void* is also done implicitly (except for function pointers and function-member / method pointers which cannot be cast to void*), but casting back requires an explicit cast.)

Asaf
  • 4,317
  • 28
  • 48
2

As per the documentation,

int pthread_join(pthread_t thread, void **retval);

So, the pthread_join takes a pointer to void* as its second argument. This is because,

In pthread_join, you get back the address passed to pthread_exit by the finished thread. If you pass just a plain pointer, it is passed by value so you can't change where it is pointing to. To be able to change the value of the pointer passed to pthread_join, it must be passed as a pointer itself, that is, a pointer to a pointer.

Now, to your question, "Why is casting done in the second instance and not in the first?" In the first instance, i.e., pthread_create, it expects a void* as its fourth argument. So passing &which_prime would be implicitly converted to void*.

In the second instance, i.e., pthread_join, it expects a void** and we are passing &prime there. So, the compiler will complain. So, to bypass the bug, the author passes a cast of void* which will be automatically converted to void**.

But this is not a good solution.

The Solution::

void* prime ; // make prime as void*
pthread_join( thread, &prime );
printf( "%" PRIxPTR "\n", (intptr_t)prime ) ; 
// intptr_t instead of int to get an integer type 
// that's the same size as a pointer
Abhineet
  • 5,320
  • 1
  • 25
  • 43
  • 1
    Down-voted because this answer is too terse and doesn’t adequately explain the asymmetry--which is the focus of the question--to someone who doesn’t already know the answer. – Chris Page Jan 04 '14 at 11:21
  • @ChrisPage:: I have now included the citation. Thanks for the feed though. – Abhineet Jan 04 '14 at 12:15
  • I don’t see how this addresses the question “Why is casting done in the second instance and not in the first?”. – Chris Page Jan 04 '14 at 12:25
  • 1
    The standard quote directly contradicts the assertion above it. A ptr-to-void is incompatible with any ptr-to-function. – Jens Jan 04 '14 at 12:26
  • Your first solution will work (except %d is not the correct conversion for intptr_t) because the thread function returns an int cast to `void*`. The second solution is wrong. – n. m. could be an AI Sep 23 '18 at 06:40
  • @n.m. Rectified as pointed out. Yes, the second soln was plainly wrong. Hence, removed it along with the irrespective quotation cited. – Abhineet Sep 24 '18 at 09:00
  • @n.m. Saw your comment below Fred's answer. It definitely makes sense. – Abhineet Sep 24 '18 at 09:03
1

I believe the same code has been referenced in other questions.

The answer in the second link explains:

It's not valid. It simply happens to work if sizeof(int) == sizeof(void *), which happens on many systems.

A void * is only guaranteed to be able to hold pointers to data objects.

Here is a C FAQ on the subject.

And the quoted text:

How are integers converted to and from pointers? Can I temporarily stuff an integer into a pointer, or vice versa?

Pointer-to-integer and integer-to-pointer conversions are implementation-defined (see question 11.33), and there is no longer any guarantee that pointers can be converted to integers and back, without change

Forcing pointers into integers, or integers into pointers, has never been good practice

Community
  • 1
  • 1