7

次のようにコンパイルするサンプル ソース ファイル test.c があるとします。

$ gcc -03 -ウォール

test.c は次のようになります。

/// CMP128(x, y)
//
// arguments
//  x - any pointer to an 128-bit int
//  y - any pointer to an 128-bit int
//
// returns -1, 0, or 1 if x is less than, equal to, or greater than y
//
#define CMP128(x, y) // magic goes here

// example usages

uint8_t  A[16];
uint16_t B[8];
uint32_t C[4];
uint64_t D[2];
struct in6_addr E;
uint8_t* F;

// use CMP128 on any combination of pointers to 128-bit ints, i.e.

CMP128(A, B);
CMP128(&C[0], &D[0]);
CMP128(&E, F);

// and so on

また、2 つの重複するポインターを渡すと、未定義の結果が得られるという制限を受け入れるとしましょう。

私はこのようなことを試しました(これらのマクロが各行の終わりにバックスラッシュでエスケープされた改行で適切にフォーマットされていると想像してください)

#define CMP128(x, y) ({
  uint64_t* a = (void*)x;
    uint64_t* b = (void*)y;

  // compare a[0] with b[0], a[1] with b[1]
})

しかし、マクロ (a[0] < b[0]) で a を逆参照すると、gcc から「逆参照は厳格なエイリアス規則を破ります」というエラーが表示されます

ユニオンを使用して、メモリ内の 1 つの場所を 2 つの異なる方法で適切に参照する必要があると考えていたので、次のようなことを試しました。

#define CMP128(x, y) ({
    union {
        typeof(x) a;
        typeof(y) b;
        uint64_t* c;
    }   d = { .a = (x) }
        , e = { .b = (y) };

    // compare d.c[0] with e.c[0], etc
})

ただし、厳密なエイリアシング規則に関して、コンパイラからまったく同じエラーが発生します。

だから:実際にメモリをコピーする以外に、厳密なエイリアスを壊さずにこれを行う方法はありますか?

( may_aliasはカウントされません。厳密なエイリアス規則をバイパスできるようにするだけです)

編集: memcmp を使用してこれを行います。エイリアシングのルールにとらわれてしまい、考えもしませんでした。

4

1 に答える 1

5

エイリアス規則は、ポインタ マジックに関係なく、アクセスしているオブジェクトのいわゆる「実効型」(つまりメモリ位置) によって決定されるため、コンパイラは正しいです。この場合、共用体を使用したポインターの型パニングは、明示的なキャストと違いはありません。標準では、任意のポインター型に互換性のある表現があることを保証していないため、実際にはキャストを使用することをお勧めします。つまり、実装定義に不必要に依存しているためです。行動。

標準に準拠したい場合は、データを新しい変数にコピーするか、元の変数の宣言中に共用体を使用する必要があります。

128 ビット整数がビッグ エンディアンまたはリトル エンディアン (つまり、混合エンディアンではない) の場合、memcmp()(直接または戻り値を否定した後に) を使用するか、自分でバイト単位の比較を行うこともできます。文字タイプはエイリアシング ルールの例外です。

于 2011-06-26T21:44:57.800 に答える