在C语言中,(void**)是什么意思?

17

我会去查找这个问题,但说实话我不知道从何处开始,因为我不知道它被称为什么。我见过像这样将变量传递给函数的:

myFunction((void**)&variable);

这让我非常困惑,因为所有这些都很熟悉;只是我以前从未见过它们像这样组合在一起。

这是什么意思?我是新手,所以尽量少用行话,谢谢!

6个回答

14

void* 是指向任何类型的指针。 void ** 是另一级间接性 - “指向指向任何类型的指针”。基本上,当您想要允许函数返回任何类型的指针时,就会传入它。

&variable 获取变量的地址。 变量应该已经是某种指针,才能使用该方法,但它可能不是 void * - 例如,它可能是 int *,因此获取其地址将导致一个int **。如果该函数需要使用 void **,则需要将其转换为该类型。

(当然,它需要实际返回正确类型的对象,否则调用代码将在后续尝试以错误方式使用它时失败。)


好的,所以调用函数必须请求类型void作为参数,以便我将对象转换为void。但我想后续问题是,数据类型void有什么好处对函数有益呢?如果void表示未知的数据类型,那么这意味着属性和值也是未知的。 - numerical25
它允许您绕过通常进行的类型检查;例如,您可以拥有一个函数,该函数接受指向函数的指针和要调用它的参数,然后返回结果。如果您必须指定类型(例如char,int等),则必须为每个参数类型编写一个函数;使用void*,您可以使用数据而不必关心实际类型是什么。 - El Yobo

8

逐个拆开它...

myFunction接受一个指向void类型的指针指针(这基本上意味着它可以指向任何东西)。它可能声明为以下内容:

myFunction(void **something);

你传入的任何内容都必须具有该类型。因此,你需要获取指针的地址,并使用(void **)进行转换,使其成为一个void指针。这基本上是将其剥离了任何关于它所指向的内容的想法 - 否则编译器可能会抱怨。

这意味着&variable是一个指针的地址(&执行此操作)- 因此variable是一个指针。指向什么?谁知道!

以下是更完整的代码片段,以便了解它们如何组合在一起:

#include <stdio.h>

int myInteger = 1;
int myOtherInt = 2;
int *myPointer = &myInteger;

myFunction(void **something){
    *something = &myOtherInt;
}

main(){
    printf("Address:%p Value:%d\n", myPointer, *myPointer);
    myFunction((void**)&myPointer);
    printf("Address:%p Value:%d\n", myPointer, *myPointer);
}

如果您编译并运行此程序,应该会得到如下输出:
Address:0x601020 Value:1
Address:0x601024 Value:2

您可以看到,myFunction改变了myPointer的值 - 这是因为它被传递了指针的地址。


哦,好的。所以请纠正我如果我错了。一个void指针只能保存任何类型的数据类型。一旦特定的数据类型转换为void指针,那么void指针就会成为该数据类型。当然,除非函数要求它,否则我不能将void指针传递到函数中。 - numerical25
指针只是一个值 - 在内存中的地址。它的类型是向编译器指示它所指向的东西的类型。void指针就像告诉编译器,您不会告诉它指针可能指向的类型。在传递参数时,您始终必须匹配函数声明要接受的类型。因此,如果函数需要一个void **,而您有一个int **,则必须将其转换为函数所期望的内容。 - Kim Reece

5
这是将指针转换为指向void类型指针的一种方法。在Windows系统中,像CoCreateInstance()这样的函数经常使用此方法。
ISomeInterface* ifaceptr = 0;
HRESULT hr = ::CoCreateInstance(CLSID_SomeImplementation, NULL, CLSCTX_ALL,
    IID_ISomeInterface, (void**)&ifaceptr);
if(SUCCEEDED(hr))
{
    ifaceptr->DoSomething();
}

该代码将指向ISomeInterface的指针转换为指向void指针,以便CoCreateInstance()可以将ifaceptr设置为有效值。

由于它是指向void指针的指针,所以根据接口ID(例如IID_ISomeInterface),该函数可以输出任何类型的指针。


这很有道理。但我从未听说过如何处理未知数据类型的变量。如果CoCreateInstance不知道ifaceptr的类型,它会如何处理?其次,void大小是否随原始数据类型的大小而任意变化。 - numerical25
CoCreateInstance总是创建一个指向COM对象的指针,并通过void**参数返回它。您可以使用IID_ISomeInterface参数告诉它您想要的类型,您需要负责正确使用类型转换将void**转换为可用的内容。 - Ken Bloom
CoCreateInstance()会根据接口ID(例如示例中的IID_ISomeInterface)“知道”输出指针应该是什么类型。此外,对于给定架构,所有指针大小都相同(例如,在32位系统上,所有指针都为4字节,而在64位系统上,所有指针都为8字节)。不存在void类型,但可以有void指针。这基本上意味着指向任何类型的指针。 - In silico
我明白你的意思。我也见过可以传递变量数据类型FLAG的变量。我以前不知道为什么,但现在我想我知道了。我还猜测你永远不能开始一个void类型的变量。例如: void* myVar。当调用需要它时,您必须先指定数据类型,然后将其转换为void类型。如果我错了,请纠正我。 - numerical25
关于void*转换,您可以始终将其转换为和从void指针转换。是否安全取决于不同的情况。例如,将Foo*强制转换为void*,然后再转换回Foo*始终是安全的,但是将Bar*强制转换为void*,然后再转换回Foo*绝对不安全(假设FooBar不相关)。这就是为什么我们通过标识符告诉CoCreateInstance()实际上void指针是哪种类型的原因。 - In silico
显示剩余2条评论

3

它是一个指向未指定类型变量的指针的指针。所有指针的大小相同,所以void*仅表示“指向某个东西的指针,但我不知道它是什么”。一个void**也可以是一个未指定类型的二维数组。


所有指针大小都相同”并不一定正确。但更准确的说法是,“指向void的指针足够大,可以存储任何类型对象的指针”。 - Toby Speight

2

这将把&variable强制转换为void**(即指向指向void的指针)。

例如,如果你有以下内容:

void myFunction(void** arg);

int* variable;

这里使用一元运算符 & 取得变量 variable 的地址,并将其传递给 myFunction()


0
变量是指向未定义(void)类型的指针。&运算符返回该变量的地址,因此您现在拥有指向某个指针的指针。因此,通过引用将指针传递到函数中。该函数可能具有更改由该指针引用的内存的副作用。换句话说,调用此函数可能会更改原始指针所引用的内容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接