2016-10-04 12 views
1

Iは整数の範囲を表すC構造を有する:構造と一定の構造

typedef struct { 
    int* data; 
    unsigned start; 
    unsigned end; 
} Range; 

Iから読み取り範囲

bool range_put(Range* const r, int value, unsigned idx) 
{ 
    if(r->start <= idx && idx < r->end) 
    { 
     r->data[idx] = value; 
     return true; 
    } 
    else 
    { 
     return false; 
    } 
} 

一関数に値を書き込むつの機能を有しています範囲

bool range_get(const Range* const r, int* const out, unsigned idx) 
{ 
    if(r->start <= idx && idx < r->end) 
    { 
     *out = r->data[idx]; 
     return true; 
    } 
    else 
    { 
     return false; 
    } 
} 

私がしたいことは、これらの機能を拡張してRang eをconst整数に変換する。

typedef struct { 
    const int* data; 
    unsigned start; 
    unsigned end; 
} ConstRange; 

どのように私はrange_get()RangeConstRangeの両方で動作させることができますの線に沿って何か?

range_get()の機能を単純に新しい機能bool crange_get(const ConstRange*...)に複製することが1つの解決策ですが、機能の数を低く抑えたいと考えています。

別の解決策は、(これが「許可」されているかどうか疑わしい)Rangeを団結することです

typedef union { 
    ConstRange const_range; 
    struct { 
     int* data; 
     unsigned start; 
     unsigned end; 
    } 
} Range; 

ここ欠点Range

に渡すとき range_get()を呼び出すための構文は少し複雑になりますということです
Range range; 
... 
range_get(&range.const_range ...); 

機能の数を減らしたり機能を複雑にしたりする他の方法はありますか?

Range range; 
ConstRange const_range; 
... 
range_get(&range ...); 
range_get(&const_range ...); 
+0

メモリを指している 'const int * 'を取得した後は、ポインタを使ってメモリを変更したり、*初期化したりすることはできません。たとえば、 'malloc'でメモリを割り当てると、初期化されていないメモリが残っています。解決策は、* once *に書き込まれた範囲内のどの要素を追跡して、それらに再度書き込むことを許可しないことです。これは、範囲内の各要素に対して1つのフラグを持つフラグの配列で実現できます。const/non-const部分に関しては、単純な論理フラグで十分です。 –

+1

@JoachimPileborg "...あなたは変更できません..." - あなたは**できません。 'const'はプログラマが保証するものであり、コンパイラによって強制されるものではありません。コンパイラはユーザーに警告することができますが、彼の足での射撃を妨げません。 – Olaf

+0

あなたは2つの値を持つ 'union'を使用できます。変更関数を除くすべての関数に 'const int *'を使用してください。 – Olaf

答えて

1

多数の機能に問題はありません。唯一の(潜在的な)問題はコードの重複です。その後range_get2()に委譲することによって、既存のbool range_get(const Range* const r, int* const out, unsigned idx)作品を作る

新機能bool range_get2(const int* data, unsigned start, unsigned end, const r, int* const out, unsigned idx)

を書く:だから、ない1が、より多くの機能を記述します。 range_get2()const int* dataまたはint* dataのいずれかを受け入れることに注意してください。それは気にしない。

新しいbool crange_get(const ConstRange*...)をご紹介し、range_get2()に委任してにしてください。

このように、もっと機能があるかもしれませんが、少なくともコードの重複はありません。

1

range_get()をRangeとConstRangeの両方で動作させるにはどうすればよいですか?

TL; DR:少なくとも直接はできません。

const - 修飾されたタイプは、const修飾タイプと "互換性"がありません。したがって、intconst intは互換性がありません。しかし、それはいくつかの目的のために違いを生むが、後者は前者の修飾されたバージョンである。

互換性のない型を参照する2つのポインタ型は互いに互換性がないため、int *const int *は互換性がありません。この場合も、一方はconst - 他方の修飾バージョンです。

異なるタグを持つ構造体型または対応する名前のメンバに互換性のある型がない構造体型も互いに互換性がないため、RangeConstRangeは互換性がありません。

それはタイプRange、またはその逆であったかのようにあなたは、このため、タイプConstRangeのオブジェクトにアクセスする準拠プログラムを書き込むことはできません。 C2011、6.5/7( "strict aliasing rule")によって禁止されています。実際には、これら2つの構造タイプの表現が対応して配置される必要はありません(実際にはほとんど確実になります)。

また、それはタイプconst int *、またはその逆であったかのようにあなたが他のタイプににそれらのポインタ型のいずれかの値を変換することができますが、あなたは、タイプint *のオブジェクトにアクセスすることができます。このような変換の振る舞いは、新しい型が古い型よりも大きな型合わせ要件を持たない限り、すべての場合において明確に定義されています。これはここでは非常に驚くべきことです。たとえば、int *から変換して得られたconst int *を逆参照することにより、intconst intとしてアクセスすることが許可されているため、このような変換が役立ちます。

したがって、あなたは@MikeNakisが示唆したように、ConstRangeRangeオブジェクトのdataメンバーを読んだ(ただし、変更しない)のための共通のバックエンドとして機能ヘルパー関数を適合する書くことができますが、受け入れ準拠した機能を記述することはできませんそれらの型のうちの1つのパラメータであり、いずれにせよ、他の型の対応する引数を収容する。

あなたが一番近ければ、組合を使うことになりますが、あなたが提示する方法ではありません。組合を使用すると、ConstRangeのようにRangeにアクセスすることはできません。また、脚注95の周りの体操の量は、これらの2つが対応するように配置される必要はないということ、そしてdata対応するタイプはありません。 実際にはで、このような処理は期待される結果を生成する可能性は非常に高いですが、それにもかかわらず、それらを使用するプログラムは未適合であり、未定義の動作に依存します。

しかし、どのユニオンメンバーがアクティブなものかを設定して後で呼び出す方法を提供することに基づいて、さまざまなユニオンベースのアプローチが動作します。考えてみましょう:

typedef struct { 
    union { 
     int *data; 
     const int *cdata; 
    }; 
    unsigned start; 
    unsigned end; 
    _Bool is_const; 
} Range; 

_Bool range_get(const Range* const r, int * const out, unsigned idx) { 
    if (r->start <= idx && idx < r->end) { 
     const int *data = r->is_const ? r->cdata : r->data; 

     *out = data[idx]; 
     return true; 
    } else { 
     return false; 
    } 
} 

あなたが一定の整数の範囲のための完全に独立した型を有することを主張した場合、あなたはまだ似た何かを行うことができます。キーは、どのユニオンのメンバーにアクセスするかを関数に伝える手段が必要なことです。

+0

ところで、 'r'と' out'パラメータを 'range_get'に' const'させることであなたのスタイルをコピーしましたが、これは有用な目的を果たさないことに注意してください。 *目的を果たす 'const'オブジェクトへのポインタ)。 –