2 回答
从技术上讲,该标准允许不同的对象指针类型具有不同的表示形式(甚至不同的大小),尽管char*
并且void*
要求具有相同的表示形式。但下面是UB:
int *ip = 0;
free(*(void**)(&ip));
仅仅是因为内存的ip
大小不必与内存的大小相同void*
,即使它是类型空指针的位模式也int*
不必与类型空指针的位模式相同void*
。如果它们不同,那么编译器当然必须插入代码以在您转换int*
为void*
或转换回时在它们之间进行转换。
在实践中,实现不会对您这样做(例如 Posix 禁止这样做)。
更重要的是,严格的别名规则不允许您char*
使用类型的左值访问对象void*
。所以在实践中,对指针表示的担忧不会破坏你的代码,但优化器实际上可能会。基本上,如果函数调用myfree((void**)(&p))
被内联,那么编译器可能会看到:
char *p = <something>;
void **data = (void**)(&p);
free(*data);
*data = NULL;
// code that reads p
优化器可以注意到它*data = NULL
正在设置一个 type 的对象void*
,而“读取 p 的代码”正在读取一个 type 的对象char*
,它被禁止与那里的另一个void*
对象别名。因此,允许重新排序指令,*data = NULL;
完全消除,或者可能其他我没有想到的事情会毁了你的一天,但如果你没有违反规则,那会加速代码。
您可以使用 MACRO 来执行此操作。与拥有功能相比,这将非常棒;我希望你知道使用 MACRO 的好处。
#define FREE_IF_NOT_NULL(x) if (x != NULL) { \
free(x); \
x = NULL; \
}