【问题标题】:why is this casting to void pointer valid?为什么这个转换为 void 指针有效?
【发布时间】:2011-07-31 12:47:56
【问题描述】:

我正在调试书中的程序。该程序似乎可以工作,但我不明白我在下面评论的一行。

#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;
}

【问题讨论】:

    标签: c pointers casting void


    【解决方案1】:

    这是无效的。如果sizeof(int) == sizeof(void *) 恰好可以工作,这在许多系统上都会发生。

    void * 只保证能够保存指向数据对象的指针。

    这是一个关于这个主题的C FAQ

    整数如何与指针相互转换? 我可以暂时 将整数填充到指针中,反之亦然?

    指针到整数和整数到指针的转换是 实现定义(见问题 11.33),并且不再有 指针可以转换为整数并返回的任何保证, 不变

    将指针强制转换为整数,或将整数强制转换为指针,从来没有 很好的做法

    【讨论】:

    • 我会退还这本书 :) 这确实是一个 hack,尽管它很常见(将值存储在指针中,取决于相等的类型宽度)。
    • POSIX 似乎确实支持这种 hack:例如,SIG_IGN
    • @Joseph Quinsey 我知道;我也阅读了 R.. 的回答,我同意。尽管如此,个人使用它还是太丑了。
    • POSIX 不支持它。 SIG_IGN 被定义(主要在 C 规范中,而不是 POSIX 中)具有“与相关函数指针类型兼容的类型”。一些实现通过转换一个小的整数值来实现这一点的事实只不过是这些实现的一个实现细节。
    【解决方案2】:

    “有效”是什么意思?

    您明确要求强制转换,语言或编译器不会阻止您。它是否有用是完全不同的事情。确实,正如您所说,candidate 不是指针,也没有指向任何有用的东西。返回值的接收者必须知道如何处理它(例如,将其转换回int,但不能保证这会给您返回原始值)。

    如果您从不需要该值,您可以只返回0。只是pthread_create 期望void*(*)(void*) 类型的函数指针作为参数,因此您的函数必须返回void*。如果您不需要它,您可以忽略它并返回任何旧值。

    (在其他情况下,您的线程函数可能选择了malloc() 一些内存,用结果数据填充它并返回指向该地址的指针,这就是为什么void* 在某种意义上是“最一般”的返回在不知道如何使用的情况下,为需要尽可能灵活的 C 函数键入类型。)

    【讨论】:

    • 如果函数结果无论如何都要被丢弃,它可能会返回 NULL,这可能比无效指针更安全。我认为它应该返回一个整数,但这确实是一个 hack。
    • @Rudy:是的,我明白了,返回值实际上是使用存储在pthread_join 中的。干净的解决方案是将返回值存储在作为参数传递给函数的预分配内存区域中。
    • 这也是我的想法。不管怎么做,都很难做好。
    【解决方案3】:

    虽然其他人认为 C 保留此强制转换的结果是正确的实现定义,但您的代码(使用 pthreads)依赖于 POSIX,这需要编译器必须退出的内存模型它打破你正在做的事情的方式。此外,所有现实世界的 POSIX 实现都是 ILP32 或 LP64,这意味着 int 的任何值都适合指针。

    虽然从正式的角度来看“丑陋”,但使用 int-to-void * 强制转换和线程参数或返回值来传递小整数数据通常是两个弊端中的较小者,另一个选择是 malloc(sizeof(int))在一个线程中,free 在另一个线程中,这不仅从根本上增加了显着的成本,而且对于仅针对分配线程和释放线程相同的情况进行调整的某些分配器来说可能是病态的。

    一种避免两败俱伤的方法:如果线程的创建者会停留一段时间并最终调用pthread_join,您可以将新线程传递给数据对象的指针在创建者的堆栈上或在创建者以其他方式拥有的内存中。然后,新线程可以在终止之前将其结果写入此位置。

    【讨论】:

      猜你喜欢
      • 2013-06-03
      • 1970-01-01
      • 2014-10-22
      • 2016-01-12
      • 1970-01-01
      • 2013-06-26
      • 2011-07-31
      • 1970-01-01
      • 2013-11-18
      相关资源
      最近更新 更多