【问题标题】:Dereferencing void pointer取消引用 void 指针
【发布时间】:2015-03-15 07:57:25
【问题描述】:

当我遇到这种代码时,我正在阅读使用pthreads.h 库的线程编程指南。不完全是这样,但问题在于 void * 指针取消引用。

#include <stdio.h>

void func(void * x) {
    printf("%d\n", (int) x);
}

int main() {
    int x = 10;
    func((void *) x);
    return 0;
}

为什么我只能使用

(int) x

用于取消引用func 中的void * 指针?

我认为它必须是这样的:

* ((int *) x)

关于main函数中这行代码的类似问题:

func((void *) x);

我什至没有在这里得到x 变量的地址。

【问题讨论】:

  • 您可能会在非常古老的代码中看到这种技术,或者在那些没有意识到自 1970 年代以来 C 已经改进的人编写的新代码中看到这种技术。 10 被“传输”为另一种类型。这种技术不可移植,可能会在各种系统上失败。
  • 请注意,在谈论void * p 之类的内容时,通常会调用pvoid-指针(指向void 的指针)。考虑到后一种约定,“p is a void *-pointer”将识别pvoid ** p
  • @MattMcNabb:传递intptr_tuintptr_t 类型的整数就可以了。
  • @alk 或者只是 int 因为那是变量的类型

标签: c pointers casting


【解决方案1】:

在执行(int) x 时,您并没有取消对x 的引用。您只需将指针(或多或少是一个地址)转换为 int,它是一个数字表示,可能会被打印出来。

编辑:顺便说一下,将x 转换为有符号整数(int)应该会给你一个编译器警告。 更多处理这个问题的正确方法是

printf("%p\n", x);

%p 是一个特殊的格式说明符,它将传递给printf 的东西解释为一个指针(地址说明符),以十六进制打印,这在处理地址时通常更有用。

EDIT2:顺便说一句,要取消引用x,您首先必须给它一个有意义的指针类型:

char a = *((char*)x); 

会将a 设置为存储在p 包含的地址中的字符。

【讨论】:

  • 这意味着我什至没有将地址保存在 void * x 参数中?我使用指针变量来发送值而不是地址?
  • @Joey74:指针实际上只包含它所指向的变量/数据的地址(因此得名 pointer)。在这种特殊(不是很典型)的情况下,指针仅用作包含地址的变量。
  • @Joey74:总之,是的。
【解决方案2】:

函数func只是打印指针x的地址。

如果您想查看func 的实际效果,请执行以下操作:

void func(void * x) {
    printf("%d\n", (int) x);
}

int main() {
    for (int i = 0; i < 5; i++) {
        void *x = malloc(100);
        func(x);
        free(x);
    }
}

此代码将分配 5 个内存块,并打印它们的地址。

【讨论】:

  • 假设 func 将生成一个线程并将 x 传递给它,只要您不确定线程​​不再使用 x 引用的内存,您就不想在 func 之外释放 x。
  • 我的意思是问题中定义的func,而不是任何功能。我现在已将其包含在答案中。这实际上只是为了说明为什么要将指针转换为整数。
  • 我喜欢这个答案。它很好地说明了事情。
【解决方案3】:

实际上,使用pthread_create 传递给线程函数的数据应该是静态数据(但是您只能使用它创建一个线程,因为它不是可重入的)或堆分配的数据,或者指针转换成别的东西。如果它是本地数据,它将在启动的线程完成之前被销毁(除非您采取特定的预防措施,例如在调用 pthread_createsame 函数中调用 pthread_join)。

这里有一些例子,假设一个全局的pthread_t thr;变量和下面的线程函数

void *run_thread(void*p) {
   int* pi = p;
   (*pi)++;
   printf("in thread data=%d\n", *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 thread\n", *(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 thread\n", *(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=%d\n", 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(包装几个值)作为参数到你的线程函数

【讨论】:

  • "... 应该 [...] 是静态数据" 我觉得这个措辞很严格。使用静态数据会使函数不可重入且非线程安全。
【解决方案4】:

当您执行(void *) x 时,您不会将指向整数的指针转换为 void 指针 - 您正在将整数转换为 void 指针。您现在有一个 void 指针,它指向的地址等于整数 x 的值。在您的函数定义中,您将其反转并将其转换回整数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-17
    • 1970-01-01
    • 2017-11-03
    • 1970-01-01
    • 1970-01-01
    • 2012-03-25
    • 2012-05-23
    • 2014-06-01
    相关资源
    最近更新 更多