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

今天当我在读别人的代码时,我看到了类似于 void *func(void* i);的东西,这里的 void*对于函数名和变量类型分别意味着什么?

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

209421 次浏览
void*

是一个“指向内存的指针,不假设存储的是什么类型”。 例如,如果要向函数传递参数,可以使用以下方法,这个参数可以有多种类型,在函数中,您将处理每种类型。

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

您可以看一下这篇关于指针 http://www.cplusplus.com/doc/tutorial/pointers/的文章,并阅读这一章: 空指针

这也适用于 C 语言。

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

这允许 void 指针从整数指向任何数据类型 值或浮点数到一个字符串。但作为交换,它们有 一个很大的限制: 他们指出的数据不能直接 解引用(这是合乎逻辑的,因为我们没有类型来解引用 因为这个原因,我们总是不得不把地址投入 指向其他指向混凝土的指针类型的 void 指针 数据类型,然后再解除引用。

使用 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));

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

C11标准(n1570)6.2.2.3 al1 p55表示:

指向 void的指针可以转换为指向任何对象的指针,也可以从指向任何对象的指针转换为指向 void的指针 指向任何对象类型的指针可以转换为指向 无效后再返回; 结果应与原件相等 指针。

您可以使用这个通用指针来存储指向任何对象类型的指针,但是不能对它使用通常的算术操作,也不能对它进行延迟。

它意味着指针你可以使用这个链接来获得更多关于指针的信息 Http://www.cprogramming.com/tutorial/c/lesson6.html

在这方面 C 是非凡的。可以说 void是虚无的。 void*是一切(可以是一切)。

就是这个小小的 *让一切变得不同。

雷内已经指出来了。void *是指向某个位置的指针。如何“解释”留给用户。

这是在 C 语言中使用不透明类型的唯一方法,可以在 glib 或通用数据结构库中找到非常突出的例子。它在“ C 接口和实现”中处理得非常详细。

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

一个 void 指针被称为泛型指针。我想用一个示例 pthread 场景来解释。

线程函数的原型是

void *(*start_routine)(void*)

Pthread API 设计器考虑了线程函数的参数和返回值。如果这些东西是泛型的,我们可以输入 cast 到 void * ,同时作为参数发送。类似地,返回值也可以从 void * 中检索(但我从未使用过 thread 函数的返回值)。

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);
}

指向 void的指针是“通用”指针类型。void *可以在没有显式强制转换的情况下转换为任何其他指针类型。不能解引用 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);

数组表达式 iArrdArrlArr在函数调用中从数组类型隐式转换为指针类型,每个表达式都从“指向 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希望它的参数指向 int,但实际上它正在使用 double。在编译时不可能捕捉到这个问题; 您只会得到一个排序错误的数组。