将参数作为 int 传递给线程 - 警告:从不同大小的整数转换为指针

Passing argument as int to thread - Warning: cast to pointer from integer different size

将参数作为 int 而不是 long 传递给 pthread_create 有什么区别?

int pthread_create(pthread_t *restrict thread,
          const pthread_attr_t *restrict attr,
          void *(*start_routine)(void*), void *restrict arg);

我从 POSIX Threads Programming 中获取了代码,它仅在将函数参数声明为 long

时起作用
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);
}

您将参数视为 long 而不是指向 long 的指针。这意味着您依赖于 longvoid * 具有相同的数据大小。如果 int 有不同的大小,这可以解释为什么会出错。在任何情况下,您都应该在参数中传递 &t 并将其相应地提取为 *threadid:

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];
    static long tid[NUM_THREADS];
    int rc;
    long t;
    for(t=0; t<NUM_THREADS; t++) {
        printf("In main: creating thread %ld\n", t);
        tid[t] = t;
        rc = pthread_create(&threads[t], NULL, PrintHello, (void *)&tid[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);
}

更新:将线程 ID 存储在数组 tid 中。

在这种情况下,问题与线程无关,而是与void *和整数类型之间的转换有关。 'c' 允许您在这些类型之间自由转换,但您必须注意转换对象的大小。

例如,在 64 位模式下的 x86 上,void* 的大小是 64 位(8 字节)。与long int的大小相同。但是,int 的大小是 32 位(4 字节)。

因此,将 int 转换为 (void*) 会将 int 符号扩展为 64 位并将其分配给 void。编译器给你一个警告,因为转换中的大小不同但这里没有违反标准。

void * 转换回 int 也会生成警告。此转换将丢失高 32 位。但是如果 void * 本身是从一个 int 赋值的,它将正确地转换为 int。但是,如果你尝试将它转换为 long 而原来的 int 是负数,你将得到一个完全不同的图片,它不会与原来的 int 相同,所有上32 位将是原始符号扩展的结果。

为了让图片更有趣,在不同的平台上会有不同的效果。例如,在 32 位平台上,long int 的大小可能与 int 的大小和 void* 的大小相同。

long int 的潜在大小通常与 void* 的大小相匹配。至少它适用于一些平台。因此,这种转换是最安全的。

因此,最好的解决方案是在编写跨平台代码时避免此类转换。

以下建议代码:

  1. 干净地编译
  2. 执行所需的功能
  3. 即使发生错误也会自行清理
  4. 正确地(使用perror())输出OPs错误消息和系统认为发生错误的文本原因stderr
  5. 使用签名:int main( void ) 因此编译器没有关于未使用参数的消息。
  6. 由于循环计数器不能小于 0,因此循环计数器使用变量类型 size_t
  7. 正确地将 pthread_t 数组初始化(为 0)为 0,因此可用于清理操作
  8. 更正了对 printf() 的调用中的格式说明符,期望变量的类型为:size_t
  9. main() 函数是否在 main() 函数退出之前正确等待线程退出
  10. 合并了一个新功能:cleanup() 以在退出程序之前正确清理任何线程。
  11. 为了便于阅读和理解,使用适当的水平间距和垂直间距。

现在,建议的代码:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define NUM_THREADS 20


void *PrintHello( void *arg ) 
{
    size_t threadNum = (size_t)arg;

    printf( "Hello World! It's me, thread #%lu!\n", threadNum );
    pthread_exit( NULL );
} 



void cleanup( pthread_t *threads )
{
    for( size_t i=0; i<NUM_THREADS; i++ )
    {
        if( threads[i] )
        {
            pthread_join( threads[i], NULL );
        }
    }
}


int main( void ) 
{
    pthread_t threads[ NUM_THREADS ] = {0};


    for( size_t t = 0; t < NUM_THREADS; t++ )
    {
        printf( "In main: creating thread %ld\n", t );
        if( pthread_create( &threads[t], NULL, PrintHello, (void *)t ) )
        {
            perror( "pthread_create() failed" );
            cleanup( threads );
            exit( EXIT_FAILURE );
        }
    }

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

这里是一个典型的 运行 提议的代码在没有错误发生时的输出:

In main: creating thread 0
In main: creating thread 1
Hello World! It's me, thread #0!
In main: creating thread 2
In main: creating thread 3
In main: creating thread 4
Hello World! It's me, thread #3!
In main: creating thread 5
Hello World! It's me, thread #4!
In main: creating thread 6
In main: creating thread 7
Hello World! It's me, thread #6!
Hello World! It's me, thread #5!
In main: creating thread 8
Hello World! It's me, thread #7!
In main: creating thread 9
Hello World! It's me, thread #8!
In main: creating thread 10
In main: creating thread 11
In main: creating thread 12
Hello World! It's me, thread #10!
Hello World! It's me, thread #9!
Hello World! It's me, thread #11!
In main: creating thread 13
In main: creating thread 14
Hello World! It's me, thread #13!
In main: creating thread 15
Hello World! It's me, thread #12!
Hello World! It's me, thread #14!
In main: creating thread 16
Hello World! It's me, thread #15!
In main: creating thread 17
In main: creating thread 18
In main: creating thread 19
Hello World! It's me, thread #19!
Hello World! It's me, thread #16!
Hello World! It's me, thread #17!
Hello World! It's me, thread #18!
Hello World! It's me, thread #1!
Hello World! It's me, thread #2!