通过void修改任何数据指针是否合法**

时间:2013-08-31 19:05:56

标签: c pointers strict-aliasing

通过void **访问指针类型是否合法?

我查看了指针别名的标准引用,但我仍然不确定这是否是合法的C:

int *array;
void **vp = (void**)&array;
*vp = malloc(sizeof(int)*10);

琐碎的例子,但它适用于我所看到的更复杂的情况。

由于我通过类型不是int *int *的变量访问char *,因此似乎不合法。我不能就此得出一个简单的结论。

相关:

3 个答案:

答案 0 :(得分:4)

没有。 void **具有特定的类型(指向指向void的指针)。即指针的基础类型是“指向虚空的指针”

存储指向int的指针时,不存储like-pointer值。需要强制转换是一个强有力的指标,你所做的不是标准的定义的行为(事实并非如此)。然而,有趣的是,可以使用常规的void*来来往往,它将表现出定义的行为。换句话说,这个:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int *array;
    void *vp = &array;
    int **parray = vp;
    *parray = malloc(sizeof(int)*10);
}

是合法的。如果我删除演员并使用apple llvm 4.2(clang),原始示例甚至不会编译,这恰好归因于不兼容的指针类型,即您问题的主题。具体错误是:

  

“不兼容的指针类型初始化'void **',表达式为'int **'”

并且理所当然。

答案 1 :(得分:2)

指向不同类型的指针可以有不同的大小。

您可以将指向任何类型的指针存储到void *中,然后您可以将其恢复,但这意味着void *必须足够大以容纳所有其他指针。

通常,不允许处理持有int *的变量,而不是像void *这样的变量。

另请注意,执行转换(例如,转换为int * malloc的结果)与处理包含int *的内存区域完全不同,例如它包含void * 1}}。在第一种情况下,编译器会在需要时通知转换,而在第二种情况下,您将向编译器提供错误信息。

在X86上,它们通常具有相同的大小,如果您只是使用指向数据的指针,那么您就是安全的(但是指向函数的指针可能会有所不同)。

关于通过void *char *完成的任何写操作的别名可以改变任何对象,因此编译器必须尽可能考虑别名。 然而,在您的示例中,您正在编写void **(另一种情况)并且编译器可以自由地忽略对int *的潜在别名效果。

答案 2 :(得分:0)

您的代码可能适用于某些平台,但它不可移植。原因是C没有指向指针类型的通用指针。在void *的情况下,标准明确允许它与其他指向完整/不完整类型的指针之间的转换,但void **不是这种情况。这意味着在您的代码中,编译器无法知道*vp的值是否从void *以外的任何类型转换,因此除了您明确的转换之外无法执行任何转换施展自己。

考虑以下代码:

void dont_do_this(struct a_t **a, struct b_t **b)
{
    void **x = (void **) a;
    *x = *b;
}

编译器不会抱怨b_t *行中从void **x = *b的隐式强制转换,即使该行试图将指针指向b_t在一个只有a_t指针的地方。错误实际上是在上一行中,它将“指向a_t指针的地方的指针”转换为“指向任何指针的地方的指针”。这就是没有隐式演员的原因。有关指向算术类型的指针的类似示例,请参阅C FAQ

然后,即使它关闭了编译器警告,你的演员也很危险,因为并非所有指针类型都可能具有相同的内部表示/大小(例如void **int *)。要使代码在所有情况下都能正常工作,您必须使用中间void *

int *array;
void *varray = array;
void **vp = &varray;
*vp = malloc(sizeof(int) * 10);