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