15

次のコードを検討してください。

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_function(サードパーティによって作成された)変更のみfield_3を許可field_4したいmy_struct。だから私は次のことをします:

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, ...);

(後に示すように)呼び出す前との間my_structでポインタをキャストするのは安全ですか?inalterable_my_structext_function

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

4 に答える 4

7

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

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

コールポイントを制御できる場合は、コピーを作成し、そのコピーへのポインターを使用して関数を呼び出してから、関心のある 2 つのフィールドを元にコピーすることをお勧めします。

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;
}

これを 1 秒間に何千回も行わない限り、パフォーマンスの低下はごくわずかです。

関数がそれに触れる場合は、ポインターベースのデータも偽造する必要があるかもしれません。

于 2012-12-03T15:04:49.403 に答える
4

C99標準によれば、2つstructのsは、宣言が同一であっても互換性のあるタイプを持ちません。セクション6.7.7.5から:

例2宣言後

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

タイプt1とが指すタイプtp1は互換性があります。タイプt1はタイプとも互換性がありますが、タイプ、、、、、またはが指すstruct s1タイプとは互換性がありません。struct s2t2tp2int

さらに、修飾子が異なる2つのタイプは互換性があるとは見なされません。

2つの修飾型が互換性を持つためには、両方が互換性のある型の同じ修飾バージョンを持っている必要があります。指定子または修飾子のリスト内の型修飾子の順序は、指定された型に影響しません。

よりクリーンなアプローチは、あなたを完全に隠し、それをあいまいなハンドル(上にある)structに置き換え、の要素を操作するための機能を提供することです。このようにして、構造を完全に制御できます。フィールドの名前を自由に変更したり、レイアウトを何度でも変更したり、フィールドの基になるタイプを変更したり、その他の通常の操作を実行したりできます。の内部レイアウトがクライアントに知られている場合は避けてください。typedefvoid*structstructstruct

于 2012-12-03T15:14:35.280 に答える
2

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

unwind によって提供されるソリューションは、非常に優れたものです。別の (そしてより明白な) 解決策は、構造を 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 ...);

上記の呼び出しでポインターも定数にしましたが、それは必須ではありません。

于 2012-12-03T16:04:06.630 に答える
-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
$ 
于 2012-12-03T15:10:12.650 に答える