2

C の厳密なエイリアシングに問題があります。GCC 4.7.1 を使用しています。

例 1:
-fstrict-aliasing -Wstrict-aliasing=3 を指定してこのコードをコンパイルすると、「警告: 型がパニングされたポインターを逆参照すると、厳密なエイリアス規則が破られる」というメッセージが表示されます。

#include <stdio.h>
#include <stdint.h>

int main(void)
{
    uint8_t a[4] = {0x01, 0x23, 0x45, 0x67};
    uint32_t b;

    b = *(uint32_t *)a;

    printf("%x\n", b);

    return(0);
}


例 2:
このコードは、-fstrict-aliasing および -Wstrict-aliasing=3 または -Wstrict-aliasing=2 または -Wstrict-aliasing=1 を指定しても警告を出さない

#include <stdio.h>
#include <stdint.h>

int main(void)
{
    uint8_t a[4] = {0x01, 0x23, 0x45, 0x67};
    uint32_t b;
    void *p;

    p = a;
    b = *(uint32_t *)p;

    printf("%x\n", b);

    return(0);
}


どちらの例も正しく機能します。

ユニオンの使用も未定義の動作であり、私の場合、 memcpy() の使用は遅すぎます。
では、最初の例は安全 (偽陽性) でしょうか、それとも 2 番目の例も安全ではありません (偽陰性) でしょうか? それとも ...?

ありがとう。

4

3 に答える 3

5

uint32_t4 からa を製造したい場合はuint8_t、次のようにします:製造します。ポインター キャストによって 1 でないものから 1 を取得しようとしないでください。提示したコードは、プラットフォームがリトル エンディアンかビッグ エンディアンかによって結果が異なります。

彼らは両方とも悪いです。提供された両方のサンプルは、何があっても安全ではありません。このようなキャストでは、データの位置合わせ要件が回避されます。「to」にキャストするものが、「from」にキャストするものよりも潜在的により制限的なアラインメントを必要とする場合は、バス エラーが発生します。最初の警告に注意してください。中間の void へのポインターは、単に問題を隠します (ほとんどの問題と同様)。

それをビルドするときに、どのバイトが「どこに」行くのかを知りたいですuin32_t

uint8_t a[4] = {0x01, 0x23, 0x45, 0x67};
uint32_t b = ((uint32_t)a[0] << 24) |
             ((uint32_t)a[1] << 16) |
             ((uint32_t)a[2] << 8) |
             (uint32_t)a[3];

これにより、エンディアンに関係なく、常にa[0] バイトがターゲット 32 ビット符号なしの上位バイトに配置され、a[1] が次のバイトに配置されます。常にbなります。0x01234567

于 2012-12-04T17:41:18.350 に答える
2

2番目の例も安全ではないと思います-コンパイラがそれを見つけて実際に同じ(1バイトでアラインされた)場所を指すほど賢くpなくavoid *アラインできないため(定義により) - 何でしょうsizeof(void)?)、警告は発行されません。

于 2012-12-04T17:37:56.197 に答える
1

どちらの場合も、元の型の符号付きバリアントでも文字型でもないuint8_t別の型 ( ) によって配列要素 (型 )にアクセスしています。uint32_t

C は、独自の型または符号付きバリアント、または文字型によってオブジェクトにアクセスする必要があると述べています。そうしないと、エイリアシング規則に違反しています。

于 2012-12-04T17:58:02.350 に答える