我收到了这条消息:
expected 'void **' but argument is of type 'char **'
当我尝试编译类似于此的东西时:
void myfree( void **v )
{
if( !v || !*v )
return;
free( *v );
*v = NULL;
return;
}
在堆栈溢出后阅读这个问题后,我发现了我认为的解决方案:
Avoid incompatible pointer warning when dealing with double-indirection - 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语句可以更好地在函数中实现,而不是弄乱我的代码。
感谢大家所有的好讨论
答案 0 :(得分:4)
不,这是不合法C,除非您将void *
对象的地址传递给myfree()
(因此您可能只保留原始定义)。
原因是在您的示例中,char *
类型的对象(test
中声明为main()
的对象)通过类型void *
的左值进行修改( *v
中的左值myfree()
。 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
两次。
答案 1 :(得分:2)
这是完全合法的,但对于阅读代码的其他人来说可能会让人感到困惑。
您还可以使用强制转换来消除警告:
myfree((void **)&rest);
这更具可读性和可理解性。
答案 2 :(得分:1)
在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程序员避开这种包装器,因为当你指向你刚刚释放的内存的指针的另一个副本时,它不会帮助你 - 这正是你需要的时候帮助
答案 3 :(得分:1)
caf的回答是正确的:不,这不合法 。正如扎克所指出的那样以这种方式违法,显然最不可能引发问题。
我发现comp.lang.c FAQ list · Question 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;
}