void *是什么意思,以及如何使用它?


147

今天,当我阅读别人的代码时,我看到类似的含义,分别对函数名称和变量类型意味着void *func(void* i);什么void*

另外,什么时候需要使用这种指针以及如何使用它?


2
您正在使用什么C书?您需要整章的更好部分。
cnicutar 2012年




malloc和获得提示calloc。手册页上继续说:“ ...返回一个指向已分配内存的指针,该指针适合任何内置数据类型。”
自动机

Answers:


175

指向的指针void是“通用”指针类型。void *可以将A 转换为任何其他指针类型,而无需显式强制转换。您不能取消引用a void *或对其执行指针算术;您必须先将其转换为指向完整数据类型的指针。

void *通常用于需要在同一代码中使用不同指针类型的地方。一个经常被引用的例子是库函数qsort

void qsort(void *base, size_t nmemb, size_t size, 
           int (*compar)(const void *, const void *));

base是数组的地址,是数组nmemb中元素的数量,size是每个元素的大小,并且compar是指向比较数组中两个元素的函数的指针。它的调用方式如下:

int iArr[10];
double dArr[30];
long lArr[50];
...
qsort(iArr, sizeof iArr/sizeof iArr[0], sizeof iArr[0], compareInt);
qsort(dArr, sizeof dArr/sizeof dArr[0], sizeof dArr[0], compareDouble);
qsort(lArr, sizeof lArr/sizeof lArr[0], sizeof lArr[0], compareLong);

阵列表达式iArrdArr以及lArr隐式从数组类型转换为在函数调用指针类型,并且每个被隐式地从“指针转换int/ double/ long”到“指针void”。

比较功能如下所示:

int compareInt(const void *lhs, const void *rhs)
{
  const int *x = lhs;  // convert void * to int * by assignment
  const int *y = rhs;

  if (*x > *y) return 1;
  if (*x == *y) return 0;
  return -1;
}

通过接受void *qsort可以使用任何类型的数组。

使用void *它的缺点是您将类型安全性丢到了窗外并迎面而来。没有什么可以防止您使用错误的比较例程:

qsort(dArr, sizeof dArr/sizeof dArr[0], sizeof dArr[0], compareInt);

compareInt期望其参数指向ints,但实际上与doubles 一起使用。在编译时没有办法解决这个问题。您将得到一个错误排列的数组。


5
实际上,不能保证void*可以将a强制转换为函数指针。但是对于数据指针,您所说的成立。
Vatine

在空指针可用之前,使用了“ char *”代替。但是void更好,因为它实际上不能直接用于更改任何东西。
user50619 '19

22

使用void *表示该函数可以采用不需要为特定类型的指针。例如,在套接字函数中,

send(void * pData, int nLength)

这意味着您可以通过多种方式调用它,例如

char * data = "blah";
send(data, strlen(data));

POINT p;
p.x = 1;
p.y = 2;
send(&p, sizeof(POINT));

所以这很像其他语言中的泛型,但是没有类型检查,对吗?
温格·森顿

3
我想这将是相似的,但是由于没有类型检查,所以犯错误可能会导致非常奇怪的结果或导致程序彻底崩溃。
TheSteve

7

就这一点而言,C非常出色。可以说void虚无void*就是一切(可以是一切)。

正是这个微小*的地方与众不同。

雷内指出了这一点。A void *是指向某个位置的指针。怎样“解释”什么留给用户。

这是在C中具有不透明类型的唯一方法。可以在glib或常规数据结构库中找到非常突出的示例。在“ C接口和实现”中对其进行了非常详细的介绍。

我建议您阅读完整的章节并尝试理解“获取它”的指针的概念。


5
void*

是“在不假设存储了哪种类型的情况下指向内存的指针”。例如,如果要向函数传递参数,则可以使用该参数,并且该参数可以具有多种类型,在函数中,您将处理每种类型。


3

您可以在http://www.cplusplus.com/doc/tutorial/pointers/中查看有关指针的文章,并阅读以下章节:无效指针

这也适用于C语言。

指针的void类型是一种特殊的指针。在C ++中,void表示不存在类型,因此void指针是指向没有类型的值的指针(因此,其长度也不确定,而解除引用的属性也不确定)。

这允许空指针指向任何数据类型,从整数值或浮点数到字符串。但是作为交换,它们具有很大的局限性:它们所指向的数据不能直接取消引用(这是逻辑上的,因为我们没有要取消引用的类型),因此,我们总是必须将地址转换为指向指针的void指针。在取消引用之前,其他一些指向具体数据类型的指针类型。


3

空指针称为通用指针。我想用一个示例pthread场景来解释。

线程函数的原型为

void *(*start_routine)(void*)

pthread API设计人员考虑了线程函数的参数和返回值。如果这些东西是通用的,我们可以在发送参数时将强制类型转换为void *。同样,可以从void *中检索返回值(但是我从未使用过线程函数的返回值)。

void *PrintHello(void *threadid)
{
   long tid;

   // ***Arg sent in main is retrieved   ***
   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++){
      //*** t will be type cast to void* and send as argument.
      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);
}

为什么调用pthread_exit(NULL);而不是return 0;在main结尾?
Seabass77


1

a void*是一个指针,但其指向的类型未指定。当您将void指针传递给函数时,您将需要知道其类型,以便稍后在使用该函数时将其强制转换回正确的类型。您将看到pthreads使用函数的示例,这些函数与示例中的原型完全一样,它们用作线程函数。然后,您可以将void*参数用作指向所选通用数据类型的指针,然后将其强制转换回该类型以在线程函数中使用。使用void指针时,您需要格外小心,因为除非您退回到真正类型的指针,否则可能会遇到各种各样的问题。


1

C11标准(n1570)§6.2.2.3al1 p55说:

指向的指针void可以转换为任何对象类型的指针或从该对象类型的指针转​​换。指向任何对象类型的指针都可以转换为void指针,然后再次返回;结果应等于原始指针。

您可以使用该通用指针存储指向任何对象类型的指针,但是不能对其使用常规的算术运算,也不能对其进行推论。


0

该函数采用指向任意类型的指针,并返回一个这样的指针。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.