rtviii
rtviii

Reputation: 897

Dereferencing void * just as (int) -- standard practice?

I was trying to print a thread's return value and discovered that I'm still quite confused by the notion of double void-pointers.

My understanding was that a void* is a pointer to any datatype that can be dereferenced with an appropriate cast, but otherwise the "levels" of referencing are preserved like with regular typed pointers (i.e. you can't expect to get the same value that you put into **(int **)depth2 by dereferencing it only once like *depth2. ).

In the code (below) that I have scraped together for my thread-return-print, however, it seems that I'm not dereferencing a void pointer at all when I'm just casting it to (int). Is this a case of an address being used as value? If so, is this the normal way of returning from threads? Otherwise, what am I missing??

(I am aware that the safer way to manipulate data inside the thread might be caller-level storage, but I'm quite interested in this case and what it is that I don't understand about the void pointer.)

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

void *myThread(void *arg)
{
    return (void *)42;
}

int main()
{
    pthread_t tid;
    void *res;                                        // res is itself a void *

    pthread_create(&tid, NULL, myThread, NULL);
    pthread_join(tid, &res);                          // i pass its address, so void** now
    printf(" %d \n", (int)res);                       // how come am i able to just use it as plain int?

    return 0;
}

Upvotes: 2

Views: 145

Answers (1)

prog-fh
prog-fh

Reputation: 16815

First of all, the purpose of pthread_join() is to update the void * given through its second argument in order to obtain the result of the thread function (a void *).
When you need to update an int as in scanf("%d", &my_var); the argument is the address of the int to be updated: an int *.
With the same reasoning, you update a void * by providing a void **.

In the specific situation of your example, we don't use the returned void * in a normal way: this is a trick!
Since a pointer can be thought about as a big integer counting the bytes in a very long row, the trick is to assume that this pointer can simply store an integer value which does no refer to any memory location.

In your example, returning (void *)42, is equivalent to saying "you will find something interesting at address 42".
But nothing has ever been placed at this address!
Is this a problem? No, as long as nobody tries to dereference this pointer in order to retrieve something at address 42.

Once pthread_join() has been executed, the variable res has been updated and contains the returned void *: 42 in this case.
We perform here the reverse-trick by assuming that the information memorised in this pointer does not refer to a memory location but is a simple integer.

It works but this is very ugly!
The main advantage is that you avoid the expensive cost of malloc()/free()

void *myThread(void *arg)
{
  int *result=malloc(sizeof(int));
  *result=42;
  return result;
}

...
int *res;
pthread_join(tid, &res);
int result=*res; // obtain 42
free(res);

A better solution to avoid this cost would be to use the parameter of the thread function.

void *myThread(void *arg)
{
  int *result=arg;
  *result=42;
  return NULL;
}

...
int expected_result;
pthread_create(&tid, NULL, myThread, &expected_result);
pthread_join(tid, NULL);
// here expected_result has the value 42

Upvotes: 3

Related Questions