便携性无效**

时间:2018-05-15 11:00:48

标签: c pointers

当我发现({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);便携?

3 个答案:

答案 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);

这是可移植的,因为tmpvoid*,因此&tmpvoid**。编译器有机会在标记为(1)和(2)的行上执行其“魔术”。请注意,如果想要正确实现void**,编译器需要做同样的事情。