In this case it sees that you are intending to delete all the threads and data elements in one go, so the accepted answer stands; but if you did want to deallocate individual thread and data items at different times, you have to have allocated then individually. For example:
pthread_t** thread; // Dynamic array of pointers to pthread_t
// Allocate pthread_t* array
threads = malloc(num_thread * sizeof(*thread));
// Allocate pthread_t elements
for( i = 0; i < num_threads; i++ )
{
thread[i] = malloc( sizeof(*thread[i]) ) ;
}
// Similarly for data...
The the clean-up is more subtle (and perhaps error prone):
free( thread[i] ) ;
thread[i] = 0 ; // Set pointer to null so element cannot be used after deletion
And if all thread elements are deleted, the thread pointer array can be deleted:
free( thread ) ;
You could have a clean-up all operation:
for( i = 0; i < num_threads; i++ )
{
free( thread[i] ) ;
thread[i] = 0 ;
}
free( thread ) ;
thread = 0 ;
Freeing a null pointer is safe, so there is no need to test if an element has previously been deleted, but you would need to have tested it before using it of course.
Note that dereferencing a null pointer is specifically trapped as a run-time error, while dereferencing a stale pointer will have non-deterministic results, and will often remain unnoticed in your code until the day you make some unrelated change causes the code to crash inexplicably - so it is good practice to set the pointer to 0 after deallocation - it will allow some bugs to be detected earlier and more easily fixed.