当我发现({void **)& ptr)时,我正在阅读http://c-faq.com/ptrs/genericpp.html; "不可移植" , 那是对的吗?因为它似乎有用......
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void *wrapper_free(void **p){
if(p){
free(*p);
*p=NULL;
}
return NULL;
}
int main() {
int *ptr=malloc(sizeof(int));
*ptr=20;
printf("%d\n",ptr);
wrapper_free((void **)&ptr); //Not portably?
printf("%d",ptr);
return 0;
}
是((void **)&amp; ptr);便携?
答案 0 :(得分:5)
只有可以隐式转换为任何其他指针(对象)的通用指针才是void*
。此指针类型在标准中具有特殊规则。这些规则不适用于void**
,它只是一种指针类型。
这意味着您无法隐式地从void**
转换为int**
,反之亦然,转换必须使用代码中的强制转换显式完成。如果/如何工作是实现定义的。意味着将会发生什么取决于系统和编译器 - 它确实不可移植(C11 6.3.2.3/7)。
要使您的代码段100%可移植,您必须写:
void* vptr = ptr;
wrapper_free(&vptr);
ptr = vptr;
答案 1 :(得分:2)
之前的答案都涵盖了为什么代码根据标准不可移植的原因。我只想添加一个可以变坏的例子。
假设目标int*
与void*
具有相同的表示形式,而void*
具有陷阱值。
现在,ptr
被赋予void*
的值,并且隐式强制转换会自动更改位表示。
假设ptr
中存储的位模式与void*
现在您使用显式强制转换传递了&ptr
void**
。
当wrapper_free
尝试取消引用p
(作为void*
)时,它会取消引用陷阱值,并且无法预测超出该点的程序行为。
相反,如果您创建void*
类型的临时变量并为其分配int*
,则会发生从int*
到void*
的转换,并且您可以保证没有碰到陷阱位表示。
希望,这有助于更好地理解问题。
答案 2 :(得分:1)
代码不可移植的原因是void*
之上的第二级间接(第二个星号)无法从编译器中获得任何特殊处理。当您将任何类型的指针转换为void*
时,编译器都知道以两种方式插入转换,因此保证构造有效。添加第二个星号时,表达式的类型不再是void*
,因此编译器不会(也不能)进行必要的调整。
以下是如何使示例中的代码可移植:
void* tmp = ptr; // (1)
wrapper_free(&tmp);
ptr = tmp; // (2)
printf("%p\n", (void*)ptr);
这是可移植的,因为tmp
是void*
,因此&tmp
是void**
。编译器有机会在标记为(1)和(2)的行上执行其“魔术”。请注意,如果想要正确实现void**
,编译器需要做同样的事情。