我有一个模块,其实现我想隐藏其客户端。
我选择声明一个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解决方案会很好。
提前致谢。
答案 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);
虽然它可能只是让它成为一个警告,而不是一个硬错误。