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 の割り当てを拒否した場合に回避できるメモリ リークが発生します。

null 以外の値をポインターに割り当てることはコンパイラーによって受け入れられないため、もう少し努力すればゼロ値でも同じことが達成できると思います。

この割り当てを無効にすることはできますか? どうすればそれを達成できますか?C++ または gcc 固有の構成要素を少し使用する必要がある場合は、それで問題ありませんが、純粋な C ソリューションが適しています。

前もって感謝します。

4

3 に答える 3

4

まず第一に、あなたの typedef が間違っています: typedef struct foo foo_t;(mainそうでなければ、コンパイラは構造体への割り当てをキャッチします)。

不透明な型の場合、次のようなことをするのが通例です: typedef struct foo *foo_t;. そうtoeしないと、投稿した例のポインターにはなりません (そのため、& で渡す必要がありました)。mallocinを考慮するとfoo_create、間違った typedef を入力したと確信しています。

次に、メモリを解放する方法を自問してください。クリーンアップ機能 ( foo_destroy) を使用することで、ですね。そして、ユーザーはこのポインターをクリーンアップ関数に渡すことになっています。

したがって、次のことを考慮してください: ユーザーが整数を代入するほど無知である場合、なぜ彼女は cleanup を忘れるほど無知ではないのでしょうか?

編集

Stéphane Gimenez はtypedef struct foo foo_t が OP が望むものであるとコメントしました。私はそれを強調したいと思います:

クライアントがそのような型のオブジェクトに対してできる唯一のことは、そのアドレスを取得して不透明なポインターを生成することです。

于 2011-08-10T18:49:41.393 に答える
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_tパラメーター(コンストラクターをエミュレートする)を渡すのではなく、割り当てられたメモリーをfoo_create()から直接返すことを試みる場合があります。

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 */
    ...
}
于 2011-08-10T20:58:43.230 に答える
0

あなたはこれについて間違って考えています。ローカルの foo_t 変数をどのように開始しますか? 入力すると

void bar(void)
{
  foo_t var;
}

var にはガベージが含まれます。きれいにする唯一の方法は、入力することです

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

または0、必要に応じて、警告を発する必要があります。したがって、質問のコードは安全ではなく、クラッシュする可能性があります。

できることは、null 以外の属性を foo_tile に追加することです。つまり、次のようになります。

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

それは防止foo_tile(NULL);し、一部のコンパイラでも

foo_t var = NULL;
foo_tile(var);

ただし、ハードエラーではなく、警告になる可能性があります。

于 2011-08-10T20:54:12.173 に答える