void * 指针是否用于 C 中的通用类型?

Are void * pointers meant for generic typing in C?

我经常看到 void 指针与其他类型的指针来回转换,想知道为什么,我看到 malloc() returns 一个 void 指针也需要在使用前转换。

我是 C 的新手,来自 Python,所以指针是一个非常新的概念,让我耳目一新。

void * 指针的目的是提供一些 "dynamic" 打字吗?

以这段代码为例,是否有效?

struct GenericStruct{
    void *ptr;
};

// use void pointer to store an arbitrary type?
void store(struct GenericStruct *strct, int *myarr){
    strct->ptr = (void *) myarr;
}

// use void pointer to load an arbitrary type as int*?
int *load(struct GenericStruct *strct){
    return (int *) strct->ptr;
}

C 缺乏函数重载。因此,大多数 C "generic" 函数,例如 qsortbsearch 使用指向 void * 的指针,以便能够处理不同类型的对象。

在 C 中,您不需要将任何类型的指针转​​换为 void * 类型的指针。并且任何类型的指针都可以分配给 void * 类型的指针而无需强制转换。

所以在 C 中,您的代码片段中的函数可以像这样重写

void store(struct GenericStruct *strct, int *myarr){
    strct->ptr = myarr;
}

int *load(struct GenericStruct *strct){
    return strct->ptr;
}

void * 的目的是为一些 C 的类型规则提供一个受欢迎的例外。除了 void * 之外,您不能在没有强制转换的情况下将一种类型的指针值分配给另一种指针类型的对象 - 例如,您不能写

int p = 10;
double *q = &p; // BZZT - cannot assign an int * value to a double *

分配给不同类型的指针时,必须显式转换为目标类型:

int p = 10;
double *q = (double *) &p; // convert the pointer to p to the right type before assigning to q

除了 void *:

int p = 10;
void *q = &p; // no cast required here.

在过去的 K&R C 中,char * 被用作 "generic" 指针类型1 - 内存分配函数 malloc/calloc/realloc所有 returned char *qsortbsearch 的回调函数采用 char * 参数等,但是因为你不能直接分配不同的指针类型,您必须添加显式转换(如果目标不是 char *,无论如何):

int *mem = (int *) malloc( N * sizeof *mem );

到处使用显式转换有点痛苦。

1989/1990 标准 (C89/C90) 引入了 void 数据类型 - 这是一种不能存储任何值的数据类型。类型 void 的表达式仅针对其副作用(如果有)2 进行评估。为 void * 类型创建了一条特殊规则,这样该类型的值可以分配给 to/from 任何其他指针类型而无需显式转换,这使其成为新的 "generic" 指针类型。 malloc/calloc/realloc 全部更改为 return void *qsortbsearch 回调现在采用 void * 参数而不是 char *,现在事情变得更干净了:

int *mem = malloc( sizeof *mem * N );

您不能取消引用 void * - 在我们上面的示例中,q 的类型为 void *,我们无法在没有转换的情况下获得 p 的值:

printf( "p = %d\n", *(int *)q );

请注意,C++ 在这方面有所不同 - C++ 不会对 void * 进行特殊处理,并且需要显式强制转换才能分配给不同的指针类型。那是因为 C++ 提供了 C 没有的重载机制。


  1. 每个对象类型 都应该 可映射到 char 的数组。
  2. 在 K&R C 中,所有函数都必须 return 一个值 - 如果您没有明确键入函数,编译器会假定它 returned int。这使得很难确定哪些函数实际上是 意味着 到 return 一个值与只有副作用的函数。 void 类型对于键入不打算 return 值的函数很方便。

Is the purpose of the void * pointer to offer some "dynamic" typing?

不,因为 C 没有动态类型。但是另一方面,您的标题是正确的 - void * 大致用作 generic 指针类型,因为该类型的指针可以指向 object 任何类型。但是没有活力,因为该术语通常应用于这种上下文,因为类型 void * 的值不包含有关它们指向的 object 类型的任何信息。

然后,使用的是指针,其中 object 指向的类型无关紧要(对于给定目的)。在某些情况下,pointed-to object 的 size 确实很重要,因此有时会与此类指针一起提供。标准 qsort() 函数是一个典型的例子。在其他情况下,例如通用链表,由用户以某种方式知道 object 指向什么类型。

然而,正如@Vlad 已经观察到的那样,在 C 中不需要在 void * 和其他 object 指针类型之间进行转换。此类转换在赋值时自动执行,在其他使用与赋值相同的规则。