繁体   English   中英

C问题:单个取消引用void **双间接指针

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM