42

GCC を使用して特定のプログラムをコンパイルするときの 2 つの警告を修正しようとしています。警告は次のとおりです。

警告: 型がパニングされたポインターを逆参照すると、strict-aliasing ルールが破られます [-Wstrict-aliasing]

そして、2人の犯人は次のとおりです。

unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));

*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);

着信_buf発信_bufは次のように定義されます。

char                    incoming_buf[LIBIRC_DCC_BUFFER_SIZE];

char                    outgoing_buf[LIBIRC_DCC_BUFFER_SIZE];

これは、私が調べてきたその警告の他の例とは微妙に異なっているようです。厳密なエイリアスチェックを無効にするよりも、問題を修正したいと思います。

ユニオンを使用するための多くの提案がありました - この場合に適したユニオンは何でしょうか?

4

8 に答える 8

62

まず、エイリアシング違反の警告が表示される理由を調べてみましょう。

エイリアシング ルールcharは、オブジェクト自体の型、符号付き/符号なしバリアント型、または文字型 ( 、signed char、 )を介してのみオブジェクトにアクセスできることを示していますunsigned char

C は、エイリアシング規則に違反すると、未定義の動作が呼び出されると言います (そうしないでください! )。

プログラムの次の行で:

unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));

incoming_buf配列の要素は型charですが、としてアクセスしていunsigned intます。実際、式の逆参照演算子の結果は*((unsigned int*)dcc->incoming_buf)unsigned intです。

これはエイリアシング ルールに違反しています。なぜなら、incoming_buf配列の要素にアクセスする権利は (上記のルールの概要を参照してください! char)signed charまたはunsigned char.

2 番目の犯人にもまったく同じエイリアシングの問題があることに注意してください。

*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);

throughのchar要素にアクセスするため、エイリアシング違反です。outgoing_bufunsigned int

提案された解決策

問題を解決するには、アクセスしたい型で配列の要素を直接定義してみてください。

unsigned int incoming_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)];
unsigned int outgoing_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)];

(ちなみに、の幅unsigned intは実装定義なので、プログラムが 32 ビットであるuint32_tと想定している場合は、使用を検討する必要があります)。unsigned int

このようにして、次のようunsigned intに type を介して要素にアクセスすることで、エイリアシング規則に違反することなくオブジェクトを配列に格納できます。char

*((char *) outgoing_buf) =  expr_of_type_char;

また

char_lvalue = *((char *) incoming_buf);

編集:

特に、プログラムがコンパイラからエイリアシング警告を受け取る理由を説明します。

于 2012-01-11T19:15:02.147 に答える
29

この問題を解決するには、しゃれやエイリアスを使用しないでください。型を読み取る唯一の「正しい」方法は、型Tを割り当て、T必要に応じてその表現を設定することです。

uint32_t n;
memcpy(&n, dcc->incoming_buf, 4);

つまり、整数が必要な場合は、整数を作成する必要があります。言語的に許される方法でそれをごまかす方法はありません。

(通常、I/O の目的で) 許可されている唯一のポインター変換は、型の既存の変数Tのアドレスをとして扱うことですchar*。むしろ、サイズ の char の配列の最初の要素へのポインターとして扱いますsizeof(T)

于 2012-01-11T19:14:35.570 に答える
6
union
{
    const unsigned int * int_val_p;
    const char* buf;
} xyz;

xyz.buf = dcc->incoming_buf;
unsigned int received_size = ntohl(*(xyz.int_val_p));

簡略化された説明 1. c++ 標準では、データを自分で整列するように試みるべきであると述べていますが、g++ は、この件に関する警告を生成するためにさらに一歩進んでいます。2. アーキテクチャ/システムおよびコード内のデータ アライメントを完全に理解している場合にのみ試してください (たとえば、上記のコードは Intel 32/64 ; アライメント 1; Win/Linux/Bsd/Mac では確実です)。 3. 上記のコードを使用する唯一の実用的な理由は、コンパイラの警告を回避することです。

于 2016-09-06T17:30:33.157 に答える
0

C キャストは機能しませんでしたが、 reinterpret_cast<> は同様の状況で私を助けてくれました。

于 2020-12-17T00:25:13.390 に答える
0

最近、プロジェクトを GCC 6 から GCC 9 にアップグレードしたところ、この警告が表示されるようになりました。プロジェクトは 32 ビット マイクロコントローラー上にあり、32 ビット マシン レジスタの個々のバイトにアクセスするための構造体を作成しました。

struct TCC_WEXCTRL_t
{
    byte    OTMX;
    byte    DTIEN;
    byte    DTLS;
    byte    DTHS;
};

そして、次のようにコーディングします。

((TCC_WEXCTRL_t *)&TCC0->WEXCTRL)->DTLS = PwmLoDeadTime;

新しいコンパイラで警告が生成されました。構造体をユニオンで元の型と組み合わせることで、警告を排除できることがわかりました。

union TCC_WEXCTRL_t
{
    TCC_WEXCTRL_Type std;
    struct  
    {
        byte    OTMX;
        byte    DTIEN;
        byte    DTLS;
        byte    DTHS;
    };    
};

は、製造元のヘッダー ファイルで提供されるメンバーTCC_WEXCTRL_Typeの型です。WEXCTRL

これが完全に準拠した修正と見なされるのか、それとも GCC がそれをキャッチできなかっただけなのかはわかりません。これがうまくいかなかった場合 (または別の GCC アップグレードに引っかかった場合)、このスレッドで実名で説明されているように、ポインター型の共用体の使用に進みます。

于 2020-10-13T01:15:56.767 に答える
-2

ポインターを unsigned にキャストしてから、ポインターに戻します。

unsigned int received_size = ntohl (*((unsigned *)((unsigned) dcc->incoming_buf)) );

于 2015-07-10T13:20:59.720 に答える