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

发布于 2024-11-29 02:07:37 字数 1316 浏览 0 评论 0原文

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

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

一切都工作正常,除了我可以将零值分配给这种类型的变量,这是我想避免的。

中的示例

这是 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!!            */
}

不透明类型实际上是一个指向动态分配结构的指针; 如果我将值 0 赋给它,则会导致内存泄漏,如果编译器拒绝将 0 赋给该不透明指针,则可以避免这种情况。

编译器不接受将非空值分配给指针,因此我认为通过多一点努力也可以实现零值。

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

提前致谢。

I have a module whose implementation I want to hide from its clients.

I chose to declare an opaque type which is actually a pointer to structure to be defined only in the implementation.

It all is working fine, except that I can assign the zero value to a variable of this type, which I want to avoid.

Here's an example in C.

header file 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 );

implementation file 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++;
}

And now here is an example client that uses the module:
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!!            */
}

The opaque type is actually a pointer to a dynamically allocated structure;
If I assign the value 0 to it I incur a memory leak which could be avoided If the compiler denied the assignment of 0 to this opaque pointer.

Assigning non-null values to the pointer isn't accepted by the compiler, so I supose that with a little more effort the same could be achieved for zero value.

Is it possible to disable this assignment? How can I achieve that?
If using some bit of C++ or gcc specific constructs is needed, I'll be ok with that, although a pure C solution would be nice.

Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

阳光下慵懒的猫 2024-12-06 02:07:37

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

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

其次,问问自己,如何释放内存?通过使用清理函数(foo_destroy),对吧?用户应该将此指针传递给清理函数。

所以考虑一下:如果用户无知到给它分配一个整数,为什么她不会无知到忘记清理

编辑

Stéphane Gimenez 评论 typedef struct foo foo_t 是OP想要的。我想强调这一点

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

First of all, your typedef is wrong: typedef struct foo foo_t; (and so it your main, otherwise the compiler will catch the assignments to structures).

For opaque types it is customary to do something like: typedef struct foo *foo_t;. Otherwise your toe wouldn't be a pointer in the example you posted (that's why you had to pass it with &). Considering the malloc in foo_create, I am pretty sure you typed the wrong typedef.

Second, ask yourself, how are you going to free memory ? By using a cleanup function (foo_destroy), right ? And the user is supposed to pass this pointer to the cleanup function.

So consider this: if the user is clueless enough to assign an integer to it, why wouldn't she be clueless enough to forget to cleanup ?

EDIT

Stéphane Gimenez commented typedef struct foo foo_t is what the OP want. I would like to underline that:

The only thing that the client can do with an object of such a type is
to take its address, to produce an opaque pointer.

抠脚大汉 2024-12-06 02:07:37

我不确定你能这样做。编译器将在 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 */
    ...
}

I'm not sure you can do it that way. The compiler would fail in main():

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

It would also fail in foo_destroy():

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

You might try returning the allocated memory from foo_create() directly rather than passing in the foo_t parameter (emulating a constructor):

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 */
    ...
}
舟遥客 2024-12-06 02:07:37

你的想法是错误的。您将如何启动本地 foo_t 变量?如果你输入

void bar(void)
{
  foo_t var;
}

,那么 var 将包含垃圾。使其干净的唯一方法是输入

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

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

您可以做的就是向 foo_tile 添加 nonnull 属性,即:

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

这将阻止 foo_tile(NULL); ,并且在某些编译器中,即使

foo_t var = NULL;
foo_tile(var);

它可能只是使其成为警告,而不是硬错误。

You're thinking about this wrongly. How would you initiate a local foo_t variable? If you type

void bar(void)
{
  foo_t var;
}

then var will contain garbage. The only way to make it clean is to type

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

or 0 if you wish, but that should emit a warning. The code in your question is thereby not safe, it might crash.

What you can do is to add the nonnull attribute to foo_tile, i.e.:

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

That will prevent foo_tile(NULL); and in some compilers even

foo_t var = NULL;
foo_tile(var);

though it'll likely just make it a warning, not a hard error.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文