将长整型转换为(void *)以传递

3

阅读 LLNL 的 pthread 教程 时,我遇到了以下示例代码

/******************************************************************************
* FILE: hello.c
* DESCRIPTION:
*   A "hello world" Pthreads program.  Demonstrates thread creation and
*   termination.
* AUTHOR: Blaise Barney
* LAST REVISED: 08/09/11
******************************************************************************/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS 5

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

我可以理解为什么要通过void *long强制转换(如果不这样做,并且您传递指向t的指针,则线程打印出的数字会混乱),我的问题是,这是否应该被认为是合法的并且总是有效?还是这只是快速实现线程最简单示例的hack方法?这是一种标准的C语言方法吗?


1
这绝对是一种hack。将整数强制转换为指针再转回来是未定义行为。 - Richard J. Ross III
2
@RichardJ.RossIII,对于intptr_tuintptr_t来说,它们并不是未定义的 :-) - obataku
3
这并不是未定义行为,而是实现定义行为。这是微妙但重要的区别。 - paxdiablo
1
C11 6.3.2.3 整数可以转换为任何指针类型。 /--/ ……结果是实现定义的,可能不正确对齐,可能不指向引用类型的实体,并且可能是陷阱表示。 在实践中,可以相当安全地依赖于这种实现定义的行为:long 可能足够大以容纳地址,并且我想不出会导致陷阱表示的任何平台。只有在尝试通过指针访问内存时才存在对齐问题。 - Lundin
我同意Lundin的观点。这是一个所有现实世界编译器都共享相同实现定义行为的问题,而且以任何其他方式将数据传递给新线程都存在重大成本(额外的同步步骤)。 - R.. GitHub STOP HELPING ICE
2个回答

4
不,从ISO C标准的角度来看,这并不严格符合要求,因为没有保证指针足够宽以容纳long。
一种可行的方式是传递指向long的指针,每个线程都有一个唯一的long(比如在数组中),或者在创建线程的线程和被创建的线程之间进行线程间通信(比如条件变量),以便后者可以在前者允许下一次线程创建之前进行复制。
然而,它不符合规范并不意味着它不能在特定实现中工作。只要您能保证void*与long之间的转换不会丢失任何信息,它就可能工作得很好。
根据C11 6.3.2.3 Pointers(虽然它与C99相比几乎没有改变):

整数可以转换为任何指针类型。除非另有规定,否则结果是实现定义的,可能未对齐正确,可能不指向所引用类型的实体,并可能是陷阱表示。

任何指针类型都可以转换为整数类型。除非另有规定,否则结果是实现定义的。如果结果无法在整数类型中表示,则行为未定义。结果不需要在任何整数类型的值范围内。


如果他在使用C99,也许应该使用intptr_t。它应该被定义了 :-) - obataku
这正是我想的,谢谢。教程的下一部分将介绍如何使用长整型数组完成相同的操作。 - tacaswell
@veer:标准要求所有指针都可以用intptr_t表示,而不是反过来。由于OP的问题是具有类型为“long”的值以传递给新线程,因此通常情况下更改类型可能不可行,尽管对于这个特定的示例,其中所有值都很小,更改类型是可以的。 - R.. GitHub STOP HELPING ICE

0

这仅适用于32位系统,在64位系统中,long的大小为32位,但void*的大小为64位长。


1
long 的大小取决于实现。它可以是32位,也可以是64位。对于所有标准来说,它甚至可以是128位。 - Chris Lutz
2
@Chris是正确的。ISO为类型设置了最小范围,有效地定义了最小宽度。但是,实现可以自由提供32位指针和四十七亿比特整数,并且类型的大小可能与系统的基本位数有关或无关。 - paxdiablo
实际上,没有任何一个64位的实现是如此愚蠢,以至于他们使用了长达64位的数据类型(或者说...?)。通常情况下,使用long long来表示64位。 - Lundin
@R.. 太棒了...希望他们在某个时候升级到C99/C11。 - Lundin
大家好,我的评论是关于pthread教程中的代码。我不认为这段代码与Windows或其他编译器有任何关系,只适用于gcc。在32位Linux内核中,即使在内核模式编程中,long的长度也等于指针的长度。 - Ilay
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接