C 问题:void** 双间接指针上的单次取消引用
我收到此消息:
expected 'void **' but argument is of type 'char **'
当我尝试编译类似的内容时:
void myfree( void **v )
{
if( !v || !*v )
return;
free( *v );
*v = NULL;
return;
}
在阅读了有关堆栈溢出的问题后,我找到了我认为的解决方案:
处理双重间接时避免出现不兼容的指针警告 - 堆栈内存溢出/a>
所以我调整成这样:
#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吗?我正在取消引用 void 指针,不是吗?
谢谢各位
编辑 2010 年 10 月 12 日下午 4:45 美国东部时间:
正如已经指出的那样,free(NULL)
是安全的,并且受到 C 标准的保护。另外,如下所述,我上面的示例不合法 C。请参阅 caf 的答案、Zack 的答案和我自己的答案。
因此,对我来说,将任何要分配的指针初始化为 NULL ,然后直接在代码中直接使用 free() 和 NULL 会更容易:
free( pointer );
pointer = NULL;
我在 myfree() 中检查 NULL 的原因如下我这样做是因为我使用 fclose() 的经验。 fclose(NULL)
可能会出现段错误,具体取决于平台(例如 xpsp3 msvcrt.dll 7.0.2600.5512),因此我(错误地)假设 free() 可能会发生同样的情况。我想,与其用 if 语句弄乱我的代码,不如在函数中更好地实现。
感谢大家的积极讨论
I got this message:
expected 'void **' but argument is of type 'char **'
when I tried to compile something similar to this:
void myfree( void **v )
{
if( !v || !*v )
return;
free( *v );
*v = NULL;
return;
}
I found what I think is a solution after reading this question on stack overflow:
Avoid incompatible pointer warning when dealing with double-indirection - Stack Overflow
So I adjusted to something like this:
#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;
}
Is this legal C? I am dereferencing a void pointer aren't I?
Thanks guys
EDIT 12/10/2010 4:45PM EST:
As it has been pointed out free(NULL)
is safe and covered by the C standard. Also, as discussed below my example above is not legal C. See caf's answer, Zack's answer, and my own answer.
Therefore it's going to be easier for me to initalize any to-be-malloc'd pointers as NULL and then later on to just free() and NULL out directly in the code:
free( pointer );
pointer = NULL;
The reason I was checking for NULL in myfree() like I did was because of my experiences with fclose(). fclose(NULL)
can segfault depending on platform (eg xpsp3 msvcrt.dll 7.0.2600.5512) and so I had assumed (mistakenly) the same thing could happen with free(). I had figured rather than clutter up my code with if statements I could better implement in a function.
Thanks everyone for all the good discussion
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
不,这不合法的C,除非您将
void *
对象的地址传递给myfree()
(所以您不妨只保留原来的定义)。原因是在您的示例中,类型
char *
的对象(在main()
中声明为test
的对象)通过void *
类型的左值(myfree()
中的左值*v
)。 C 标准第 6.5 条规定:由于
void *
和char *
不是兼容类型,因此此约束已被打破。两种指针类型兼容的条件在§6.7.5.1中描述:为了达到你想要的效果,你必须使用一个宏:(
不需要检查
NULL
,因为free(NULL)
是合法的。请注意,这个宏计算p
两次)。No, this is not legal C, unless you pass the address of a
void *
object tomyfree()
(so you might as well just keep your original definition).The reason is that in your example, an object of type
char *
(the object declared astest
inmain()
) is modified through an lvalue of typevoid *
(the lvalue*v
inmyfree()
). §6.5 of the C standard states:Since
void *
andchar *
are not compatible types, this constraint has been broken. The condition for two pointer types to be compatible is described in §6.7.5.1:To achieve the effect you want, you must use a macro:
(There is no need to check for
NULL
, sincefree(NULL)
is legal. Note that this macro evaluatesp
twice).这是完全合法的,但可能会让阅读您代码的其他人感到困惑。
您还可以使用强制转换来消除警告:
这更具可读性和可理解性。
This is perfectly legal but can get confusing for other people who read your code.
You could also use casting to eliminate the warning:
This is more readable and understandable.
在 C 中,你别无选择,只能在此处引入强制转换。我将使用宏来确保在调用站点正确完成事情:
[实际上,您可以将函数和宏命名为“myfree”,这要归功于 C 的无无限宏递归规则!但这会让人类读者感到困惑。根据caf的回答下面的长篇讨论,我还将规定这里的语句
*ptr = 0
通过void**
别名修改未知类型的对象,这是运行时- 未定义的行为 - 然而,我的明智观点是,它不会在实践中引起问题,并且它是普通 C 中可用的最不坏的选择; caf 的宏对它的参数求值两次似乎更有可能(对我来说)引起真正的问题。]在 C++ 中,您可以使用模板函数,它在三个方面更好:它避免了需要在调用站点获取任何内容的地址,它不会破坏类型正确性,并且如果您不小心将非指针传递给
myfree
,您将收到编译时错误而不是运行时崩溃。当然,在 C++ 中,您还有更好的选择,例如智能指针和容器类。
最后,应该提到的是,熟练的 C 程序员会避开这种包装器,因为当您刚刚释放的内存的指针的另一个副本挂在某处时,它对您没有帮助 - 而这正是您需要帮助的时候。
In C you have no choice but to introduce a cast somewhere in here. I would use a macro to ensure that things were done correctly at the call site:
[You could actually name both the function and the macro "myfree", thanks to C's no-infinite-macro-recursion rules! But it would be confusing for human readers. Per the long discussion below caf's answer, I will also stipulate that the statement
*ptr = 0
here modifies an object of unknown type through avoid**
alias, which is runtime-undefined behavior -- however, my informed opinion is, it will not cause problems in practice, and it's the least bad option available in plain C; caf's macro that evaluates its argument twice seems far more likely (to me) to cause real problems.]In C++ you could use a template function, which is better on three counts: it avoids needing to take the address of anything at the call site, it doesn't break type correctness, and you will get a compile-time error instead of a run-time crash if you accidentally pass a non-pointer to
myfree
.But of course in C++ you have even better options available, such as smart pointer and container classes.
It should, finally, be mentioned that skilled C programmers eschew this kind of wrapper, because it does not help you when there's another copy of the pointer to the memory you just freed hanging around somewhere -- and that's exactly when you need help.
咖啡馆的答案是正确的:不,这是不合法的。正如扎克指出的那样,以这种方式违法显然最不可能引起问题。
我在 comp.lang.c 常见问题列表·问题 4.9,它指出必须使用中间 void 值。
caf's answer is correct: No, it's not legal. And as Zack points out breaking the law in this way is apparently least likely to cause problems.
I found what appears to be another solution in the comp.lang.c FAQ list · Question 4.9, which notes that an intermediate void value has to be used.