关于无效的澄清*

clarification on void *

因为我们只能将一个指针转换为另一个指针,那么“(void *) t 和 (int)threadid”在下面的代码中究竟是如何工作的? 我们不应该使用 &t 而不是 t 吗?

void *PrintHello(void *threadid)
{
   long tid;
   tid = (long)threadid;
   printf("Hello World! It's me, thread #%ld!\n", tid);
   pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
   pthread_t threads[NUM_THREADS];
   int rc;
   long t;
   for(t=0;t<NUM_THREADS;t++){
     printf("In main: creating thread %ld\n", t);
     rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
     if (rc){
       printf("ERROR; return code from pthread_create() is %d\n", rc);
       exit(-1);
       }
     }

   /* Last thing that main() should do */
   pthread_exit(NULL);
}

这里,

 rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);

最后一个参数 a long (t) 中的强制转换为 void * (这是传递给线程函数的参数)。转换完成是因为 pthread_create() 接受 void * 作为最后一个参数。

该值再次从 void * 转换回 long 值:

   tid = (long)threadid;

但是这种转换完全是实现定义的,并且可能允许作为陷阱表示(因此未定义)。所以这个转换是不安全的。

您可以使用临时数组:

  long arr[NUM_THREADS];

  for(t=0;t<NUM_THREADS;t++){
     printf("In main: creating thread %ld\n", t);
     arr[t] = t; 
     rc = pthread_create(&threads[t], NULL, PrintHello, &arr[t]);
     ...

但这涉及确保数组 arr 生命周期在线程接收到值之前没有结束。这可以在主线程中使用 pthread_join() 或使用 malloced 指针(而不是使用本地数组 arr)或使用具有静态存储持续时间的数组来完成,例如使 arr 全球。

为了扩展我的评论,C 明确规定指针可以转换为整数,反之亦然。除了值为 0 的整数常量(可靠地转换为 NULL 指针)外,结果是实现定义的。这种转换只需要按照我下面的附录中的描述是可逆的。

然而,实际上,对于某些 n,指针表示通常采用等同于 n 位无符号整数的形式.在这些情况下,通常情况下您可以安全地在指针和足够宽度的整数之间来回转换而不会丢失信息。更有可能(但仍然不能保证)您可以从(不太宽的)整数到指针并返回而不会丢失信息,即使您可能无法安全地执行指针 -> 整数 -> 指针。符合 C 的实现将记录所有实现定义的行为的详细信息,包括这些。

您提供的程序依赖于此类转换;它们可以在许多环境中工作,但它们是否真的可以工作取决于所使用的 C 实现。正如您所建议的那样,通过传递 真正的 指针(并将其作为一个指针使用)可以避免这种实现依赖性。

更新补充:

在评论中,@IanAbbott 提出了类型 intptr_t,C2011 是这样描述的:

a signed integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer [...] optional.

-- C2011, 7.20.1.4

提供此类型的符合规范的实现将确保其关于转换的行为符合规定,但由于实现提供它是可选的,因此这与 [=41 的实现指定性质不冲突=] 转换(在 C2011 6.3.2.3/3-6 中有描述)。即使是提供这种类型的实现也不因此接受涉及其他整数类型(或其他指针类型)的转换的任何义务。

另请注意,尽管 intptr_tuintptr_t(如果可用)提供了从指针到整数再到指针的往返转换,但它的规定与整数到指针再到整数的转换无关,就标准而言。