C - 线程函数:将 (void*) 转换为 (long) 并且代码有效?但是怎么办?
C - Threads Function: Casting a (void*) to a (long) and code works? But how?
下面函数printHello接收一个空指针作为参数。 但是这个指针被转换为 long 并且代码有效。我不认为我理解这种转换是如何工作的。指针类型不应该保存地址吗?
long 类型如何突然兼容转换为指针类型,反之亦然?
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#define NUM_OF_THREADS 5
void *printHello (void *thread_id)
{
long tid;
tid = (long) thread_id; // Why is this possible?
printf("hello from thread #%ld!", tid);
pthread_exit(NULL);
}
int main()
{
pthread_t threads[NUM_OF_THREADS];
int return_code;
long i;
for(i=0; i<NUM_OF_THREADS; i++)
{
printf("In main: creating thread %ld\n", i);
return_code = pthread_create(&threads[i],NULL,printHello,(void*) i);
// Why does it allow to cast 'long i' into '(void*) i'?
if(return_code)
{
printf("Error: return code from pthread_create is %d\n", return_code);
exit(-1);
}
}
pthread_exit(NULL);
}
示例输出:
In main: creating thread 0
In main: creating thread 1
hello from thread #0!
hello from thread #1!
In main: creating thread 2
In main: creating thread 3
hello from thread #2!
In main: creating thread 4
hello from thread #3!
hello from thread #4!
with (void*) i
你在欺骗编译器,让它相信 i
是一个地址。强迫演员表总是有所作为。不代表推荐。
它有点工作(只要你不试图取消引用该值)因为 void *
具有相同的大小或大于 long
,但它绝对是实现定义的并且应该是避免了。
您应该将指针传递给您的数据(没关系,因为您的变量在 main
中声明并且具有兼容的范围)
return_code = pthread_create(&threads[i],NULL,printHello,&i);
并在您的线程代码中取消引用它:
void *printHello (void *thread_id)
{
long tid = *((long *)thread_id);
}
整数可以转换为任何指针类型,任何指针类型都可以转换为整数类型,因为语言标准是这么说的。地址和整数最终都是固定长度的位集合,所以没有什么不可能的。
这种转换的结果是实现定义的,但在实践中往往效果很好。标准备注 "mapping functions for converting a pointer to an integer or an integer to a pointer are intended to be consistent with the addressing structure of the execution environment".
除了一些不寻常的表示,如果两种类型具有相同的数据位数,则转换应该双向进行。该标准指定指针可以转换为 intptr_t
或 uintptr_t
并再次转换回来,这在实践中通常意味着这些类型至少与指针一样大。许多旧代码使用 long
用于相同目的。似乎无法保证该程序所需的另一个方向的往返安全。虽然它通常有效。
下面函数printHello接收一个空指针作为参数。 但是这个指针被转换为 long 并且代码有效。我不认为我理解这种转换是如何工作的。指针类型不应该保存地址吗? long 类型如何突然兼容转换为指针类型,反之亦然?
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#define NUM_OF_THREADS 5
void *printHello (void *thread_id)
{
long tid;
tid = (long) thread_id; // Why is this possible?
printf("hello from thread #%ld!", tid);
pthread_exit(NULL);
}
int main()
{
pthread_t threads[NUM_OF_THREADS];
int return_code;
long i;
for(i=0; i<NUM_OF_THREADS; i++)
{
printf("In main: creating thread %ld\n", i);
return_code = pthread_create(&threads[i],NULL,printHello,(void*) i);
// Why does it allow to cast 'long i' into '(void*) i'?
if(return_code)
{
printf("Error: return code from pthread_create is %d\n", return_code);
exit(-1);
}
}
pthread_exit(NULL);
}
示例输出:
In main: creating thread 0
In main: creating thread 1
hello from thread #0!
hello from thread #1!
In main: creating thread 2
In main: creating thread 3
hello from thread #2!
In main: creating thread 4
hello from thread #3!
hello from thread #4!
with (void*) i
你在欺骗编译器,让它相信 i
是一个地址。强迫演员表总是有所作为。不代表推荐。
它有点工作(只要你不试图取消引用该值)因为 void *
具有相同的大小或大于 long
,但它绝对是实现定义的并且应该是避免了。
您应该将指针传递给您的数据(没关系,因为您的变量在 main
中声明并且具有兼容的范围)
return_code = pthread_create(&threads[i],NULL,printHello,&i);
并在您的线程代码中取消引用它:
void *printHello (void *thread_id)
{
long tid = *((long *)thread_id);
}
整数可以转换为任何指针类型,任何指针类型都可以转换为整数类型,因为语言标准是这么说的。地址和整数最终都是固定长度的位集合,所以没有什么不可能的。
这种转换的结果是实现定义的,但在实践中往往效果很好。标准备注 "mapping functions for converting a pointer to an integer or an integer to a pointer are intended to be consistent with the addressing structure of the execution environment".
除了一些不寻常的表示,如果两种类型具有相同的数据位数,则转换应该双向进行。该标准指定指针可以转换为 intptr_t
或 uintptr_t
并再次转换回来,这在实践中通常意味着这些类型至少与指针一样大。许多旧代码使用 long
用于相同目的。似乎无法保证该程序所需的另一个方向的往返安全。虽然它通常有效。