а указатель на указатель для чего используется? (мне они всегда взрывали мозг)
Я видел и указатель на указатель на указатель. Самый простой пример — функция main. Там есть char * argv[] ну или char * * argv, получается, что у нас каждая строка это указатель на символы, а массив строк и есть массив указателей, и указатель на указатель используется. Ещё, например, https://man7.org/linux/man-pages/man3/pthread_join.3.html
По задумке авторов библиотеки pthread, поток при завершении возвращаетуказатель void *. Когда мы ждём поток, когда поток завершится, функция возвращает код ошибки (0 если всё хорошо, и EINVAL, если либо потока дождаться нельзя, либо мы что-то намудрили. Аргумент дескриптор потока. Ну и нам, в общем-то было бы неплохо узнать, а что вернул поток? Вот мы и передаём void * * чтобы по этому указателю записать указатель.
void * ret;
int status = pthread_join(thread, &ret);
if (status != 0) { // Какая-то неведомая фигня
}
// А тут ret это указатель, который вернул поток, можем с ним что-то сделать