2012-12-03 14 views
15

次のコードを考慮してください。Cの構造体ポインタ間のキャスト

typedef struct{ 
    int field_1; 
    int field_2; 
    int field_3; 
    int field_4; 

    uint8_t* data; 
    uint32_t data_size; 
} my_struct; 

void ext_function(inalterable_my_struct* ims, ...); 

私は(第三者によって書かれた)ext_functionmy_structにのみfield_3field_4を変更できるようにします。だから私は、次の操作を行います。

typedef struct{ 
    const int field_1; 
    const int field_2; 
    int field_3; 
    int field_4; 

    const uint8_t* data; 
    const uint32_t data_size; 
} inalterable_my_struct; 

void ext_function(inalterable_my_struct* ims, ...); 

それは(後に示すように)ext_functionを呼び出す前my_structinalterable_my_struct間のポインタをキャストしても安全ですか?

void call_ext_function(my_struct* ms){ 
    inalterable_my_struct* ims = (inalterable_my_struct*)ms; 
    ext_function(ims, ...); 
} 
+0

'ext_function'が' data'を変更しないようにするには、 'const uint8_t * const data;'と宣言する必要があります。 – Henrik

答えて

7

私はこれは良い考えではないと思います。

呼び出された関数は、いつでもconst:nessをキャストして、必要に応じてデータを変更することができます。

あなたがcallpointsを制御することができた場合、それはコピーを作成する方がよいとコピーへのポインタで関数を呼び出すでしょう、そして、あなたが気に二つのフィールドをコピーバック:

void call_ext_function(my_struct* ms) 
{ 
    my_struct tmp = *ms; 
    ext_function(&tmp, ...); 
    ms->field_3 = tmp.field_3; 
    ms->field_4 = tmp.field_4; 
} 

非常にクリーン、これを何千回も実行しない限り、パフォーマンスペナルティは実際には軽微でなければなりません。

ポインタベースのデータにも機能が偽装されている必要があります。

-2

標準では何も言わないにもかかわらず、ほとんどのコンパイラーで動作するでしょう。あなたが本当にする必要があれば、おそらくユニオンでもっと移植可能な何かをすることさえできます。それ以外は何も変わらない。

これは、それは何も変わりません理由です。

$ cat foo.c 
struct foo { 
    const int a; 
    int b; 
}; 

void 
foo(struct foo *foo) 
{ 
    foo->a = 1; 
} 
$ cc -c foo.c 
foo.c: In function ‘foo’: 
foo.c:9: error: assignment of read-only member ‘a’ 
$ cc -Dconst= -c foo.c 
$ 
-2

にするために使用メンバーへの書き込みをconst安全でない可能性があります。これらは、コンパイラ/リンカとオペレーティングシステムによって読み取り専用メモリに格納されている可能性があります。 How is read-only memory implemented in C?を参照してください。

4

C99標準によれば、2つのstructは、宣言が同一であっても互換性のある型を持たないだろう。セクション6.7.7.5から:

例2宣言後

typedef struct s1 { int x; } t1, *tp1; 
typedef struct s2 { int x; } t2, *tp2; 

タイプt1tp1によって指さタイプは互換性があります。タイプt1もタイプstruct s1と互換性がありますが、struct s2,t2tp2、またはintが指すタイプと互換性がありません。

また、異なる修飾子を持つ2つのタイプは互換性が考慮されていない2つの修飾型が互換であるために

は、両方とも互換性のあるタイプの同じ修飾バージョン をもたなければなりません。指定子または修飾子のリスト内の型修飾子の順序は、指定された型には影響しません( )。

クリーナーアプローチは、あいまいなハンドル(void*の上typedef)と交換し、structの要素を操作するための機能を提供し、完全にあなたのstructを非表示にするだろう。こうすることで、structの構造を完全に制御できるようになります。自由にフィールドの名前を変更したり、レイアウトを頻繁に変更したり、フィールドの基本タイプを変更したり、 structの内部レイアウトがクライアントに知られている場合は、通常は避けてください。

2

構造がキャストされているかどうかを追跡するのが難しいため(特にコードが大きい場合)、これは良い考えではないと思います。それをconstにキャストしても、後でnon-const structureにキャストされないことは保証されません。

アンワインドによって提供されるソリューションは、非常に優れたソリューションです。別の(そしてもっと明白な)ソリューションは、構造を2つの小さな部分に分割することです。

typedef struct{ 
    const int field_1; 
    const int field_2; 
    const uint8_t* data; 
    const uint32_t data_size; 
} inalterable_my_struct; 

typedef struct{ 
    int field_3; 
    int field_4; 
} my_struct; 

void ext_function(const inalterable_my_struct* const ims, my_struct* ms ...); 

上記の呼び出しでポインタを一定にしていますが、これは必須ではありません。

関連する問題