我一直在思考。我没有找到任何直接回答这个问题的东西,但我想我知道答案;我只想得到一些经验丰富的人的一些意见。
的已知,:
void指针只指向一个内存地址。它不包含任何类型信息。
int指针指向包含int的内存地址。它将读取指向整数的内存地址中的任何内容,无论最初填充到地址中的是什么。
问题:
如果void双指针void ** foo
指向动态分配的void指针数组
void ** foo = malloc(sizeof(void *) * NUM_ELEMENTS);
正如我所假设的那样,因为void指针的独特性质实际上缺少任何类型的信息,而不是void ** foo
等效语句
void * bar = malloc(sizeof(void *) * NUM_ELEMENTS);
当我通过分配特定类型(例如使用
)来使用间接访问时(有人指出我无法取消引用无效指针。为了清楚问题的目的,下一行被更改为适合该信息)
int ** fubar = bar;
我会从单个 void指针得到一个适当的指针,它只是表现,就像 double 指针一样?
或者这一切都在我脑海中?
答案 0 :(得分:2)
允许将malloc
的结果分配给void *
对象,然后将其分配给int **
对象。这是因为malloc
的返回值无论如何都具有类型void *
,并且保证适合分配指向具有基本对齐要求的任何类型对象的指针。
但是,这段代码:
#define NUM_ELEMENTS 1000
void *bar = malloc(sizeof(void *) * NUM_ELEMENTS);
int **fubar = bar;
*fubar = 0;
不保证C标准有效;它可能有不确定的行为。原因并不明显。 C标准不要求不同类型的指针具有相同的大小。 C实现可以将int *
的大小设置为一百万字节,将void *
的大小设置为四个字节。在这种情况下,分配给1000 void *
的空间不足以容纳一个int *
,因此对*fubar
的分配具有未定义的行为。通常,人们会以这种方式实现C只是为了证明一个观点。但是,类似的错误可能在较小的范围内:有C实现,其中不同类型的指针具有不同的大小。
如果指针具有适合目标类型的对齐方式,则指向对象类型的指针可以转换为指向另一种对象类型的指针。如果是,则将其转换回来产生具有原始值的指针。因此,您可以将指向void *
的指针转换为指向void
并返回的指针,并且您可以将指针转换为void *
指针指向int *
并返回指针,前提是对齐方式合适(如果指针由malloc
返回,并且您没有使用具有扩展对齐的自定义对象,则它们将会出现。)
通常,您不能使用指向对象类型的指针进行编写,然后使用指向不同对象类型的指针读取相同的字节。这违反了别名规则。一个例外是,如果其中一个指针是字符类型。此外,许多C实现确实支持这种别名,但可能需要设置命令行选项来启用此类支持。
这种对别名的禁止包括重新解释指针。请考虑以下代码:
int a;
int *b = &a;
void **c = (void **) &b;
void *d = *c;
int *e = (int *) d;
在第四行中,c
指向b
占用的字节,但*c
尝试将这些字节解释为void *
。这不保证能够正常工作,因此d
得到的值不一定是指向a
的指针,即使它在最后一行转换为int *
也是如此。
答案 1 :(得分:0)
在C标准下,您给出的代码的行为是未定义的,因为您分配了一个void
指针数组,然后尝试将其用作int
指针数组。标准中没有任何内容要求这两种指针具有相同的大小或对齐方式。现在,如果你说过
void * bar = malloc(sizeof(int*) * NUM_ELEMENTS);
int ** fubar = bar;
然后一切都会好的。
现在,在绝大多数计算机上,int*
和void*
实际上具有相同的大小和对齐方式。所以你的代码应该在实践中运作良好。
此外,这两者并不相同:
void ** foo = malloc(sizeof(void *) * NUM_ELEMENTS);
void * bar = malloc(sizeof(void *) * NUM_ELEMENTS);
这是因为foo
可以在任何元素处取消引用以获取void指针,而bar
则不能。例如,此程序是正确的,并在我的32位计算机上打印00000000
:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
void **a = calloc(10, sizeof(void*));
printf("%p\n", a[0]);
return 0;
}
另一点是,您似乎认为类型信息在机器级别的指针中是显式的。这不是真的(至少对于绝大多数实现而言)。 C指针的类型通常仅在编译程序时表示。在编译完成时,显式类型信息通常会丢失,除非调试符号表是不可运行的代码。 (这有一些小的例外。对于C ++,情况非常不同。)