如何禁用整数赋值(0)到一个不透明的变量?

时间:2011-08-10 18:39:43

标签: c opaque-pointers

我有一个模块,其实现我想隐藏其客户端。

我选择声明一个opaque类型,它实际上是一个只在实现中定义的结构的指针。

一切正常,但我可以将零值分配给这种类型的变量,我想避免这种变量。

这是C中的一个例子。

头文件foo.h

/* foo.h */
typedef struct foo *foo_t; /* <- sorry this was obviously flawed, the '*' was missing */


extern void foo_create( foo_t *t );
extern void foo_destroy( foo_t *t );
extern void foo_tile( foo_t x );

实施文件foo.c

/* foo.c */

#include <stdlib.h>

#include "foo.h"

struct foo {
    int some_member;
};

void foo_create( foo_t *t )
{
    if ( *t==0 ) {
        *t = malloc( sizeof(struct foo) );         
    }
}

void foo_destroy( foo_t *t )
{
    if ( *t!=0 ) {
        free(*t);
        *t    = 0;
    }
}


void foo_tile( foo_t t )
{
    t->some_member++;
}

现在这是一个使用该模块的示例客户端: bar.c:

#include "foo.h"

int main( int argc , char **argv )
{
    foo_t toe;

    foo_create( &toe );
    toe    = 0;  /* <-- How to make the compiler (gcc) refuse this? */
    toe    = 1;  /* <--- the compiler rejects this YAY!!            */
}

opaque类型实际上是指向动态分配结构的指针; 如果我为其赋值0,则会导致内存泄漏,如果编译器拒绝为此不透明指针赋值0,则会发生内存泄漏。

编译器不接受为指针指定非空值,因此我稍加努力,可以实现零值的同样。

是否可以禁用此分配?我怎样才能做到这一点? 如果需要使用一些C ++或gcc特定的构造,我会好的,尽管纯C解决方案会很好。

提前致谢。

3 个答案:

答案 0 :(得分:4)

首先,你的typedef是错误的:typedef struct foo foo_t;(所以它是你的main,否则编译器将捕获结构的赋值。)

对于不透明类型,通常会执行以下操作: typedef struct foo *foo_t; 。否则你的toe将不会是你发布的示例中的指针(这就是你必须用&amp;传递它的原因)。考虑malloc中的foo_create,我很确定您输入了错误的typedef。

其次,问问自己,如何你要释放记忆?通过使用清理功能(foo_destroy),对吧?并且用户应该将此指针传递给清理功能。

所以考虑一下:如果用户无能为其分配一个整数,那么为什么她不能无法忘记清理

修改

StéphaneGimenez评论 typedef struct foo foo_t是OP想要的。我想强调that

  

客户端可以对这种类型的对象做的唯一事情是   获取其地址,以产生不透明的指针。

答案 1 :(得分:1)

我不确定你能这样做。编译器在main()中失败:

    toe    = 0;  /* <-- How to make the compiler (gcc) refuse this? */

在foo_destroy()中也会失败:

    void foo_destroy( foo_t *t )
    {
        if ( *t!=0 ) {
            free(*t);
            *t    = 0;  /* compiler refuses this also */
        }
    }

您可以尝试直接从foo_create()返回已分配的内存,而不是传入foo_t参数(模拟构造函数):

extern foo_t * foo_create( void );

foo_t * foo_create( void )
{
    foo_t * t;

    t = malloc( sizeof(struct foo) );  

    return(t);       
}

int main( int argc , char **argv )
{
    foo_t * toe;

    toe = foo_create();

    toe = 0; /* Clearly a memory leak via reassignment */
    ...
}

答案 2 :(得分:0)

你错误地想到了这一点。你会如何发起一个本地的foo_t变量?如果您输入

void bar(void)
{
  foo_t var;
}

然后var将包含垃圾。使其清洁的唯一方法是键入

void bar(void)
{
  foo_t var = NULL;
}

0如果您愿意,但应该发出警告。因此,您的问题中的代码不安全,可能会崩溃。

你可以做的是将nonnull属性添加到foo_tile,即:

void foo_tile( foo_t t ) __attribute__((nonnull(1)));

这将阻止foo_tile(NULL);,甚至在某些编译器中

foo_t var = NULL;
foo_tile(var);

虽然它可能只是让它成为一个警告,而不是一个硬错误。