web-dev-qa-db-ja.com

「型で区切られたポインターを逆参照すると、厳密なエイリアス規則が破られる」という警告

Enum *をint *にキャストするコードを使用します。このようなもの:

enum foo { ... }
...
foo foobar;
int *pi = reinterpret_cast<int*>(&foobar);

コード(g ++ 4.1.2)をコンパイルすると、次の警告メッセージが表示されます。

dereferencing type-punned pointer will break strict-aliasing rules

このメッセージをグーグルで検索したところ、厳密なエイリアスの最適化がオンになっている場合にのみ発生することがわかりました。次の質問があります。

  • この警告とともにコードを残すと、潜在的に間違ったコードが生成されますか?
  • この問題を回避する方法はありますか?
  • 存在しない場合、ソースファイル内から厳密なエイリアスをオフにすることは可能ですか? )?

そして、はい、実際にこの種のエイリアシングが必要です。

53
petersohn

順番に:

  • はい。 GCCは、ポインターがエイリアスできないと想定します。たとえば、一方から割り当ててから他方から読み取る場合、GCCは最適化として読み取りと書き込みを並べ替えることができます。これは実稼働コードで発生するので、デバッグするのは好ましくありません。

  • いくつか。ユニオンを使用して、再解釈する必要があるメモリを表すことができます。 _reinterpret_cast_を使用できます。メモリを再解釈する時点で_char *_を介してキャストできます。_char *_は何でもエイリアスできると定義されています。 __attribute__((__may_alias__))を持つ型を使用できます。 -fno-strict-aliasingを使用して、エイリアシングの仮定をグローバルにオフにできます。

  • 使用される型の__attribute__((__may_alias__))は、おそらくコードの特定のセクションの仮定を無効にするのに最も近いものです。

特定の例では、enumのサイズが正しく定義されていないことに注意してください。 GCCは通常、それを表現するために使用できる最小の整数サイズを使用するため、enumへのポインターを整数として再解釈すると、結果の整数に初期化されていないデータバイトが残る可能性があります。それをしないでください。なぜ適切に大きな整数型にキャストしないのですか?

57
moonshadow

しかし、なぜあなたはこれをしているのですか? sizeof(foo)!= sizeof(int)の場合は破損します。列挙型が整数に似ているからといって、それが1つとして格納されるわけではありません。

そのため、「潜在的に」間違ったコードを生成する可能性があります。

11
CashCow

次のコードを使用してデータをキャストできます。

template<typename T, typename F>
struct alias_cast_t
{
    union
    {
        F raw;
        T data;
    };
};

template<typename T, typename F>
T alias_cast(F raw_data)
{
    alias_cast_t<T, F> ac;
    ac.raw = raw_data;
    return ac.data;
}

使用例:

unsigned int data = alias_cast<unsigned int>(raw_ptr);
11
Markus Lenger

この答え を調べましたか?

厳密なエイリアスルールにより、このセットアップは無効になり、2つの無関係なタイプが同じメモリを指すことはできません。 char *のみがこの特権を持っています。残念ながら、この方法でコーディングすることはできますが、警告が表示される場合がありますが、コンパイルは正常に完了します。

5
icecrime

厳密なエイリアスはコンパイラオプションであるため、メイクファイルから無効にする必要があります。

そして、はい、それは不正確なコードを生成する可能性があります。コンパイラは、foobarpiが結合されていないと効果的に想定し、*piは、foobarが変更されても変更されません。

既に述べたように、static_cast代わりに(およびポインターなし)。

2
Let_Me_Be