问题
I'm debugging a program from a book. The program appears to work but I do not understand one line which I comment below.
#include <pthread.h>
#include <stdio.h>
/* Compute successive prime numbers (very inefficiently). Return the
Nth prime number, where N is the value pointed to by *ARG. */
void* compute_prime (void* arg)
{
int candidate = 2;
int n = *((int*) arg);
while (1) {
int factor;
int is_prime = 1;
/* Test primality by successive division. */
for (factor = 2; factor < candidate; ++factor)
if (candidate % factor == 0) {
is_prime = 0;
break;
}
/* Is this the prime number we’re looking for? */
if (is_prime) {
if (--n == 0)
/* Return the desired prime number as the thread return value. */
return (void*) candidate; // why is this casting valid? (candidate is not even a pointer)
}
++candidate;
}
return NULL;
}
int main ()
{
pthread_t thread;
int which_prime = 5000;
int prime;
/* Start the computing thread, up to the 5,000th prime number. */
pthread_create (&thread, NULL, &compute_prime, &which_prime);
/* Do some other work here... */
/* Wait for the prime number thread to complete, and get the result. */
pthread_join (thread, (void*) &prime);
/* Print the largest prime it computed. */
printf(“The %dth prime number is %d.\n”, which_prime, prime);
return 0;
}
回答1:
It's not valid. It simply happens to work if sizeof(int) == sizeof(void *)
, which happens on many systems.
A void *
is only guaranteed to be able to hold pointers to data objects.
Here is a C FAQ on the subject.
How are integers converted to and from pointers? Can I temporarily stuff an integer into a pointer, or vice versa?
Pointer-to-integer and integer-to-pointer conversions are implementation-defined (see question 11.33), and there is no longer any guarantee that pointers can be converted to integers and back, without change
Forcing pointers into integers, or integers into pointers, has never been good practice
回答2:
What do you mean by "valid"?
You're explicitly requesting a cast, and the language or the compiler won't stop you. Whether it's useful is an entirely different matter. Indeed, as you say, candidate
isn't a pointer and it's not pointing to anything useful. The recipient of the return value will have to know what to do with it (e.g. cast it back to int
, though it's not guaranteed that that gives you back the original value).
If you never need the value, you might just return 0
. It's simply that pthread_create
expects as an argument a function pointer of type void*(*)(void*)
, so your function has to return a void*
. If you don't need it, you can ignore it and just return any old value.
(In other situations, your thread function might have chosen to malloc()
some memory, fill it with result data and return a pointer to that address, which is why void*
is in some sense the "most general" return type for a C function that needs to be as flexible as possible without knowing how it's going to be used.)
回答3:
While others are right that C leaves the results of this cast implementation-defined, your code (using pthreads) depends on POSIX, which requires a memory model where a compiler would have to go out of its way to break what you're doing. Also all real-world POSIX implementations are ILP32 or LP64 meaning that any value of int
will fit in a pointer.
While "ugly" from a formal standpoint, using int
-to-void *
casts with the thread argument or return value to pass small integer data is usually the lesser of two evils, the other choice being malloc(sizeof(int))
in one thread and free
in the other, which not only fundamentally adds significant costs, but also can be pathologically bad on some allocators tuned only for the case where the allocating thread and freeing thread are the same.
One way you can avoid both evils: if the creator of the thread will be sticking around for a while and eventually calling pthread_join
, you could pass the new thread a pointer to a data object on the creator's stack or in memory otherwise owned by the creator. The new thread can then write its result to this location before it terminates.
来源:https://stackoverflow.com/questions/6889568/why-is-this-casting-to-void-pointer-valid