в вашем коде есть условие гонки, оно может выглядеть "иногда" нормально:
void take_client(int sock) { //sock live on stack here
pthread_t thread_id;
pthread_create(&thread_id, NULL, thread_func, (void*)&sock);
// you pass the stack address of sock to your thread
}
Адрес, который вы передаете, должен оставаться действительным во время работы потока, так что вы можете себе это позволить (этовроде "глобальный" где-то), либо вам нужно выделить новый буфер и передать его потоку.
Иногда вы увидите код, приводящий значение sock к void * ((void *) sock) и приведение обратноуказатель на int в потоке.
Это может сработать, но я думаю, что выделение нового буфера лучше для удобочитаемости и дает четкое представление о том, кто отвечает за этот буфер (с общим «носком»).для обеспечения полной безопасности между потоками потребуется блокировка).
Более того, обычно вам приходится передавать в поток гораздо больше информации, таким образом уже имея буфер, облегчающий развитие вашего кода.