Du kannst es auch etwas anders betrachten, hier nochmal die Signatur von pthread_create:
Code:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);
Oder etwas umformuliert:
Code:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          out_t *(*start_routine) (in_t *), in_t *arg);
start_routine ist also eine Funktion, die einen Pointer auf etwas (in_t) bekommt, und später einen Pointer auf etwas anderes (out_t) zurück gibt. Wie verhält sich pthread_join dazu? Nun, in der obigen Darstellung würde pthread_join so aussehen:
Code:
int pthread_join(pthread_t thread, out_t **retval);
Du hast also irgendwo einen Pointer A, der später auf das Ergebnis zeigen soll. Und übergibst einen Pointer auf diesen Pointer A, damit pthread_join den Wert von Pointer A entsprechend setzt.

Randnotiz: Du kannst den Rückgabewert nicht ohne vorherige Überprüfung nutzen, näheres dazu verrät die Doku

Grüße,
Markus