当我遇到这种代码时,我正在阅读使用pthreads.h
库进行线程编程的指南。不完全是这样,但事情在于指针取消引用void *
。
#include <stdio.h>
void func(void * x) {
printf("%dn", (int) x);
}
int main() {
int x = 10;
func((void *) x);
return 0;
}
为什么我可以只使用
(int) x
用于取消引用void *
func
指针?
我认为它必须是这样的:
* ((int *) x)
关于主函数中这行代码的类似问题:
func((void *) x);
我什至没有得到x
变量的地址。
在执行(int) x
时,您不会取消引用x
。您只需将指针(或多或少是地址)转换为int
,这是一种数字表示形式,可能会打印出来。
编辑:顺便说一下,将x
转换为有符号整数(int
)应该会给你一个编译器警告。处理此问题的更正确方法是
printf("%pn", x);
%p
是一种特殊的格式说明符,它将交给printf
的东西解释为指针(地址说明符),以十六进制打印,这在处理地址时通常更有用。
EDIT2:顺便说一下,要取消引用x
,您首先必须给它一个有意义的指针类型:
char a = *((char*)x);
将a
设置为存储在p
包含的地址处的字符。
该函数func
只打印指针x
的地址。
如果要查看func
的实际操作,请执行以下操作:
void func(void * x) {
printf("%dn", (int) x);
}
int main() {
for (int i = 0; i < 5; i++) {
void *x = malloc(100);
func(x);
free(x);
}
}
此代码将分配 5 个内存块,并打印它们的地址。
在实践中,传递给具有pthread_create
的线程函数的数据应该是静态数据(但随后您只能使用它创建一个线程,因为它不是可重入的)或堆分配的数据,或转换为其他内容的指针。如果它是本地数据,它将在启动的线程完成之前被销毁(除非您采取特定的预防措施,例如在调用pthread_create
的同一函数中调用pthread_join
)。
下面是一些示例,假设全局pthread_t thr;
变量和以下线程函数
void *run_thread(void*p) {
int* pi = p;
(*pi)++;
printf("in thread data=%dn", *pi);
return pi;
}
一个包含静态数据的线程
回想一下,对于静态数据,您的代码不是可重入的,并且您只能有一个线程使用该数据。
void run_my_thread(void) {
static int x;
int err = pthread_create(&thr, NULL, run_thread, &x);
if (err)
{ fprintf(stderr, "pthread failed %d (%s)n", err, strerror(err));
exit(EXIT_FAILURE);
}
do_something_else();
void* res = NULL;
pthread_join(thr, &res);
if (res) printf("got %d from threadn", *(int*)res);
}
一种常见的模式可能是拥有一个静态数组,例如 5 个数据元素,并运行 5 个线程,每个线程在该静态数组中处理自己的元素。
具有堆分配数据的线程
void run_my_thread(void) {
int *pi = malloc(sizeof(int));
if (!pi) {perror("malloc"); exit(EXIT_FAILURE); };
*pi = 42;
int err = pthread_create(&thr, NULL, run_thread, pi);
if (err)
{ fprintf(stderr, "pthread failed %d (%s)n", err, strerror(err));
exit(EXIT_FAILURE);
}
do_something_else();
void* res = NULL;
pthread_join(thr, &res);
if (res) printf("got %d from threadn", *(int*)res);
free (pi), pi = NULL;
}
有时,但这很丑陋,您可能会将强制转换的 inptr_t
值(而不是指向它的指针)传递给 pthread_create
。
包含intptr_t
数据的线程
当然,在线程例程中将其用作取消引用的指针是没有意义的,这可能是:
void*run_thread_int(void*p) {
intptr_t i = (intptr_t)p;
printf("i=%dn", i);
return NULL;
}
然后你可以编码
void run_my_thread_int(void) {
intptr_t j=53;
int err = pthread_create(&thr, NULL, run_thread_int, (void*)j);
if (err)
{ fprintf(stderr, "pthread failed %d (%s)n", err, strerror(err));
exit(EXIT_FAILURE);
}
do_something_else();
pthread_join(thr, NULL);
}
考虑pthread_create
的一种实用方法是将线程函数视为在机器寄存器中获取单个参数,该参数可以容纳指针(或具有上述技巧的intptr_t
)。
大多数情况下,您将使用堆分配的struct
(打包多个值)作为线程函数的参数。
当您执行(void *) x
时,您不是将指向整数的指针转换为 void 指针 - 您是在将整数转换为 void 指针。现在,您有一个 void 指针,其指向的地址等于整数 x
的值。在函数定义中,你反转它并将其转换回整数。