例如,如果我想编写一个可以使指针变为空闲的“免费”,我可以编写如下内容:
void myfree(void **data) {
free(*data);
*data = NULL;
}
然而,当我尝试写这个时,我得到一个编译器警告(来自gcc 4.6.2)说:warning: passing argument 1 of ‘myfree’ from incompatible pointer type [enabled by default] ... note: expected ‘void **’ but argument is of type ‘char **‘
(在这种情况下,我释放一个char数组)。
void*
似乎是特殊的,以避免此类警告,因为calloc
,free
等不会触发此类警告,但void**
不是(鉴于上述情况)。唯一的解决方案是明确的演员,还是我误解了什么?
[我正在重新审视最近一个项目中的一些痛点,想知道如何更好地处理这些问题,所以我们正在考虑角落案件,因此今天的C问题。]
更新鉴于void*
是特殊情况,我可以使用void*
并在myfree
内投射,但这可能是一个有点不负责任的解决方案因为每个人和他们的狗都会将指针传递给看似free
的东西,所以我需要基于“间接程度”的某种编译器警告,这是一个实用的解决方案。因此,通用“指针指针”的想法。
答案 0 :(得分:5)
从技术上讲,标准允许不同的对象指针类型具有不同的表示(甚至不同的大小),尽管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
正在设置类型为void*
的对象,而“读取p的代码”正在读取类型为char*
的对象,这是禁止的从那里的其他void*
对象别名。因此,允许对指令进行重新排序,完全消除*data = NULL;
,或者可能还有其他我想不到的会破坏你一天的事情,但如果你没有违反规则,这会加速代码。< / p>
答案 1 :(得分:-1)
您可以使用MACRO执行此操作。与具有功能相比,这将是非常好的;我希望你知道使用MACRO的优势。
#define FREE_IF_NOT_NULL(x) if (x != NULL) { \
free(x); \
x = NULL; \
}