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?

2
  • 7
    pthread_join's second argument is a void**. That code looks wrong. Commented Dec 9, 2013 at 11:54
  • 1
    Looking at the full code (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 **). Commented Jan 4, 2014 at 13:53

5 Answers 5

16

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 *.
Sign up to request clarification or add additional context in comments.

2 Comments

"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*?
@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 ....
14

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.

1 Comment

The void** parameter of pthread_join is an out-parameter. It makes no sense to initialise primep. Its value will be ignored and overwritten.
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.)

Comments

3

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 

7 Comments

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.
@ChrisPage:: I have now included the citation. Thanks for the feed though.
I don’t see how this addresses the question “Why is casting done in the second instance and not in the first?”.
The standard quote directly contradicts the assertion above it. A ptr-to-void is incompatible with any ptr-to-function.
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.
|
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

2 Comments

In C99 pointers can be safely converted to intptr_t/uintptr_t and back.
@FredFoo if they exist. They're optional in the standard

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.