2

未定義のC++の動作に関する記事のシフト演算子のセクションで読んだものに混乱しています。

ARMアーキテクチャでは、シフト演算子は、オペランドサイズに関係なく、常に256ビットのパターン空間で行われるかのように動作します。つまり、パターンは256の位置ごとに繰り返されるか、「ラップアラウンド」します。これの別の考え方は、パターンが256を法として指定された数の位置にシフトされることです。もちろん、結果にはパターン空間の最下位ビットのみが含まれます。

テーブルは特に奇妙です:

Given a 32-bit integer with a value of 1:
+-----------------------------------+
| Shift left    ARM    x86    x64   |
+-----------------------------------+
| 32            0      1      1     |
| 48            0      32768  32768 |
| 64            0      1      1     |
+-----------------------------------+

これらの値は何ですか、そしてなぜそれらが重要なのですか?

シフト演算子はラップしません。C ++仕様によると、32ビット値を32だけ左にシフトすると、結果は常に0になります (編集:間違っています。答えを参照してください!)では、この記事は何を取得しているのでしょうか。未定義動作とは何ですか?

このコードをx86で実行すると、次のようになります0

printf("%d", 1 << 32);

おそらく、このコードスニペットは問題を示しています。

// C4293.cpp
// compile with: /c /W1
unsigned __int64 combine (unsigned lo, unsigned hi) {

    return (hi << 32) | lo;   // C4293

    // try the following line instead
    // return ( (unsigned __int64)hi << 32) | lo;
}

プログラマーがすべてのビットloをシフトアウトしたので、戻り値はになると思います。hiこれはおそらく間違いだったので、警告はいいですが、未定義の動作は見られません...

4

3 に答える 3

4

値をシフトするためにx86またはx64マシン命令を使用する場合、それらはシフト量をマスクし、実際のシフトには下位ビットのみを使用します。他のいくつかのハードウェアはそれをしないかもしれません。

それが未定義の理由です。

リテラルを使用した例で1 << 32は、コンパイラが値を計算する可能性があり、それがである理由です0。実際のx86ハードウェアで操作を試してみると、次のようになります1

于 2012-10-26T13:32:15.637 に答える
3

C ++仕様によれば、32ビット値を32だけ左にシフトすると、結果は常に0になります。

いいえ、それは標準が言っていることではありません。32ビットタイプを32以上シフトすることは未定義の動作です(5.8 / 1)

ARM(257ビット以上のタイプはありません)で256ビットシフトすることは未定義の動作であるため、CPUはその時点で完全にラップする権利があります。

于 2012-10-26T13:33:49.093 に答える
3

結果のタイプは、プロモートされた左オペランドのタイプです。右のオペランドが負の場合、またはプロモートされた左のオペランドのビット単位の長さ以上の場合、動作は未定義です。

これはC++11の§5.8/1からのものです。intが32ビットの場合、32だけシフトすることはできません(左オペランドの符号に関係なく、右シフトまたは左シフト)。

于 2012-10-26T13:26:30.010 に答える