[英]C question: single dereference on a void** double indirection pointer
我收到了这条消息:
expected 'void **' but argument is of type 'char **'
当我尝试编译类似于此的东西:
void myfree( void **v )
{
if( !v || !*v )
return;
free( *v );
*v = NULL;
return;
}
在读取堆栈溢出问题后,我发现了我认为的解决方案:
处理双间接时避免不兼容的指针警告 - Stack Overflow
所以我调整到这样的事情:
#include <stdio.h>
#include <stdlib.h>
void myfree( void *x )
{
void **v = x;
if( !v || !*v )
return;
free( *v );
*v = NULL;
return;
}
int main( int argc, char *argv[] )
{
char *test;
if( ( test = malloc( 1 ) ) )
{
printf( "before: %p\n", test );
myfree( &test );
printf( "after: %p\n", test );
}
return 0;
}
这是合法的C吗? 我解除引用无效指针不是吗?
多谢你们
编辑12/10/2010美国东部时间下午4:45:
正如已经指出的那样, free(NULL)
是安全的并且被C标准所涵盖。 另外,如下所述,我上面的例子不合法C.请参阅caf的答案,Zack的答案和我自己的答案。
因此,我可以更容易地将任何要成为malloc的指针初始化为NULL,然后直接在代码中直接释放free()和NULL:
free( pointer );
pointer = NULL;
我在myfree()中检查NULL的原因与我一样是因为我使用fclose()的经验。 fclose(NULL)
可以根据平台进行段错(例如xpsp3 msvcrt.dll 7.0.2600.5512),因此我假设(错误地)使用free()可能会发生同样的事情。 我想用if语句可以更好地在函数中实现,而不是弄乱我的代码。
感谢大家所有的好评
不,这不是合法的C,除非你将void *
对象的地址传递给myfree()
(所以你也可以保留原来的定义)。
原因是在您的示例中, char *
类型的对象(在main()
声明为test
的对象)通过void *
类型的左值myfree()
的lvalue *v
)进行修改。 C标准的第6.5条规定:
7对象的存储值只能由具有以下类型之一的左值表达式访问:
— a type compatible with the effective type of the object, — a qualified version of a type compatible with the effective type of the object, — a type that is the signed or unsigned type corresponding to the effective type of the object, — a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object, — an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or — a character type.
由于void *
和char *
不是兼容类型,因此该约束已被破坏。 两种指针类型兼容的条件在§6.7.5.1中描述:
要使两个指针类型兼容,两者都应具有相同的限定条件,并且两者都应是兼容类型的指针。
要获得所需的效果,必须使用宏:
#define MYFREE(p) (free(p), (p) = NULL)
(不需要检查NULL
,因为free(NULL)
是合法的。请注意,此宏评估p
两次)。
这是完全合法的,但对于阅读代码的其他人来说可能会让人感到困惑。
您还可以使用强制转换来消除警告:
myfree((void **)&rest);
这更具可读性和易懂性。
在C中,你别无选择,只能在这里介绍一个演员阵容。 我会使用宏来确保在呼叫站点上正确完成的事情:
void
myfree_(void **ptr)
{
if (!ptr || !*ptr) return;
free(*ptr);
*ptr = 0;
}
#define myfree(ptr) myfree_((void **)&(ptr))
[你可以实际命名函数和宏“myfree”,这要感谢C的无限宏递归规则! 但这对人类读者来说会让人感到困惑。 根据caf的回答下面的长篇讨论,我还将规定语句*ptr = 0
这里通过void**
别名修改一个未知类型的对象,这是运行时未定义的行为 - 但是,我的知情意见是,它将在实践中不会引起问题,而且它是普通C中可用性最差的选项; 在两次评估其论点的caf的宏看起来更有可能(对我而言)引起真正的问题。
在C ++中,您可以使用模板函数,这在三个方面更好:它避免需要在调用站点获取任何内容的地址,它不会破坏类型的正确性,并且您将得到编译时错误而不是如果你不小心将非指针传递给myfree
则运行时崩溃。
template <typename T>
void
myfree(T*& ptr)
{
free((void *)ptr);
ptr = 0;
}
但是当然在C ++中你有更好的选择,比如智能指针和容器类。
最后,应该提到熟练的C程序员避开这种包装器,因为当你需要另外一个指向你刚刚释放的内存指针的副本时,它就无法帮助你 - 而这正是你需要帮助的时候。
caf的回答是正确的:不,这不合法 。 正如扎克所指出的那样以这种方式违法,显然最不可能引发问题。
我在comp.lang.c FAQ列表中找到了似乎是另一个解决方案。 问题4.9 ,其中指出必须使用中间空值。
#include <stdio.h>
#include <stdlib.h>
void myfree( void **v )
{
if( !v )
return;
free( *v );
*v = NULL;
return;
}
int main( int argc, char *argv[] )
{
double *num;
if( ( num = malloc( sizeof( double ) ) ) )
{
printf( "before: %p\n", num );
{
void *temp = num;
myfree( &temp );
num = temp;
}
printf( "after: %p\n", num );
}
return 0;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.