例如,如果我想编写一个将指针清空的“free”,我可以编写如下内容:
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 问题。]
update鉴于void*
是特殊情况,我可以使用它来解决这个问题void*
并在里面投射myfree
,但这将是一个有点不负责任的解决方案,因为每个人和他们的狗都会传递一个指向看起来像这样的东西的指针free
,所以我需要某种基于“间接程度”的编译器警告,才能成为一个实用的解决方案。因此就有了通用“指向指针的指针”的想法。
从技术上讲,该标准允许不同的对象指针类型具有不同的表示形式(甚至不同的大小),尽管char*
and void*
要求具有相同的表示。但下面是UB:
int *ip = 0;
free(*(void**)(&ip));
只是因为记忆ip
不需要与内存大小相同void*
,即使它是类型空指针的位模式int*
不需要与类型的空指针的位模式相同void*
。如果它们不同,那么每当您转换时,编译器当然必须插入代码以在它们之间进行转换int*
to 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;
完全,或者可能其他我没有想到的事情会毁了你的一天,但如果你没有违反规则,这会加快代码的速度。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)