通过 void **
访问指针类型是否合法?
我查看了关于指针别名的标准引用,但仍不确定这是否是合法的 C 语言操作:
int *array;
void **vp = (void**)&array;
*vp = malloc(sizeof(int)*10);
虽然这只是一个简单的例子,但它也适用于我所遇到的更复杂的情况。
看起来这可能不合法,因为我通过一个类型不是int *
或char *
的变量访问了一个int *
。我无法得出简单的结论。
相关:
通过 void **
访问指针类型是否合法?
我查看了关于指针别名的标准引用,但仍不确定这是否是合法的 C 语言操作:
int *array;
void **vp = (void**)&array;
*vp = malloc(sizeof(int)*10);
虽然这只是一个简单的例子,但它也适用于我所遇到的更复杂的情况。
看起来这可能不合法,因为我通过一个类型不是int *
或char *
的变量访问了一个int *
。我无法得出简单的结论。
相关:
没有专门的类型指针指向“指向void的指针”,也就是说,指针的底层类型是“指向void的指针”。
当存储指向整数的指针时,不能存储类似指针值。需要强制转换的事实表明,你正在进行的操作不符合标准(并且确实如此)。有趣的是,你可以使用常规的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),你原来的例子甚至无法编译,因为存在不兼容的指针类型,也就是你问题的主题。具体的错误信息是:
"Incompatible pointer types initializing 'void **' with an expression of type 'int **'"
这是正确的。
不同类型的指针可以有不同的大小。
你可以将任何类型的指针存储到 void *
中,然后可以恢复它,但这意味着 void *
必须足够大以容纳所有其他指针。
通常不允许像处理保存了 int *
的变量一样来处理它,认为它确实是一个 void *
。
还要注意,进行强制转换(例如,将 malloc
的结果转换为 int *
)与将包含 int *
的内存区域视为包含 void *
是完全不同的两件事。在第一种情况下,如果需要,编译器会被通知转换,但在第二种情况下,您向编译器提供了错误信息。
但是,在 X86 上,它们通常具有相同的大小,如果您只是使用数据指针进行操作,则是安全的(尽管函数指针可能不同)。
关于别名问题,通过 void *
或 char *
进行的任何写操作都可以改变任何对象,因此编译器必须考虑别名作为可能的情况。然而,在您的示例中,写入 void **
(另一种不同的事情)的任何操作,编译器都可以忽略对 int *
的可能别名效果。
int *x = (int *)malloc(...)
,编译器知道你正在进行转换并可以正确地存储值。当然,malloc
需要返回一个可用于任何对象的指针。如果你说*p = malloc(...)
,其中p
是void **
,那么编译器将执行原始写操作,认为p
指向的内存是void *
,即使需要转换也不会执行。 - 6502int*
能够容纳malloc()的返回值,即void*
,那么可以推断出void*
和int*
具有相同的大小。除非我混淆了,哈哈 :-P - Nikos C.malloc
转换为int *
时,它告诉编译器,“将此值更改为int *
”。当分配给另一个变量时,该变量是int *
,它知道不再执行任何转换,因为类型相同。*p = malloc(...)
会导致编译器在没有转换的情况下进行写入,正确的。如果目标大小不正确,则会出现问题。 - ashvoid*
比int*
要宽,那么malloc()只需确保不会返回一个适合于int*
的值。因此当截断发生时(就是一个转换的实际情况),结果仍然是正确的。 - Nikos C.你的代码可能在某些平台上可以工作,但它不具备可移植性。原因是C语言没有通用的指向指针的类型。在 void *
的情况下,标准明确允许将其与其他指向完整/不完整类型的指针进行转换,但这并不适用于 void **
。这意味着在你的代码中,编译器无法知道 *vp
的值是否从除了 void *
以外的任何类型进行了转换,因此除了你自己显式强制转换之外,编译器不能执行任何转换。
考虑以下代码:
void dont_do_this(struct a_t **a, struct b_t **b)
{
void **x = (void **) a;
*x = *b;
}
*x = *b
行对 定义为 的隐式转换没有抱怨,即使该行试图将指向的指针放在只能放置指向的指针的地方。事实上错误出现在前一行,该行将“指向指针可以放置指向的地方”的指针转换为“指向任何东西都可以放置的指针”。这就是为什么不存在隐式转换的原因。有关指向算术类型的指针类似的例子,请参见C FAQ。void **
和int *
)。为了使您的代码适用于所有情况,您必须使用中间变量void *
:int *array;
void *varray = array;
void **vp = &varray;
*vp = malloc(sizeof(int) * 10);