为什么这个到 void 指针的转换是有效的?
我正在调试书上的程序。该程序似乎可以工作,但我不明白我在下面评论的一行。
#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;
}
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;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这是无效的。如果
sizeof(int) == sizeof(void *)
,它恰好可以工作,这在许多系统上都会发生。void *
仅保证能够保存指向数据对象的指针。这是关于该主题的 C 常见问题解答。
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.
你所说的“有效”是什么意思?
您明确请求强制转换,语言或编译器不会阻止您。它是否有用是完全不同的事情。事实上,正如您所说,
candidate
不是一个指针,它没有指向任何有用的东西。返回值的接收者必须知道如何处理它(例如将其转换回int,尽管不能保证这会返回原始值)。如果您从不需要该值,则可能只返回
0
。只是pthread_create
需要一个void*(*)(void*)
类型的函数指针作为参数,因此您的函数必须返回一个void*< /代码>。如果不需要它,可以忽略它并仅返回任何旧值。
(在其他情况下,您的线程函数可能选择
malloc()
一些内存,用结果数据填充它并返回指向该地址的指针,这就是为什么void*
从某种意义上说,C 函数的“最通用”返回类型需要尽可能灵活,但不知道如何使用它。)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 toint
, 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 thatpthread_create
expects as an argument a function pointer of typevoid*(*)(void*)
, so your function has to return avoid*
. 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 whyvoid*
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.)虽然其他人认为 C 保留此转换的结果实现定义是正确的,但您的代码(使用 pthreads)依赖于 POSIX,这需要一个编译器必须不遗余力地使用内存模型打破你正在做的事情。此外,所有现实世界的 POSIX 实现都是 ILP32 或 LP64,这意味着
int
的任何值都适合指针。虽然从正式的角度来看“丑陋”,但使用
int
-to-void *
转换与线程参数或返回值来传递小整数数据通常是两害相权取其轻,另一种选择是一个线程中的malloc(sizeof(int))
和另一个线程中的free
,这不仅从根本上增加了显着的成本,而且还可能对一些分配器仅针对以下情况进行调整分配线程和释放线程是相同的。可以避免这两害的一种方法是:如果线程的创建者会停留一段时间并最终调用
pthread_join
,您可以向新线程传递一个指向创建者堆栈上或创建者拥有的内存中的数据对象。然后,新线程可以在终止之前将其结果写入此位置。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 beingmalloc(sizeof(int))
in one thread andfree
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.