web-dev-qa-db-ja.com

C ++での右シフトの未定義の動作

Cppreference.comから:

符号なしaおよび負でない値を持つ符号付きaの場合、a >> bの値はa/2の整数部分ですb 。負のaの場合、a >> bの値は実装定義です(ほとんどの実装では、算術右シフトを実行するため、結果は負のままです)。

いずれの場合も、右のオペランドの値が負であるか、プロモートされた左のオペランドのビット数以上である場合、動作は未定義です。

右のオペランドが昇格された左のオペランドのビット数以上である場合、なぜ未定義の動作になるのですか?
結果は0(少なくとも符号なし/正の整数の場合)になるはずです...

特に、g ++(バージョン4.8.4、Ubuntu)の場合:

unsigned int x = 1;
cout << (x >> 16 >> 16) << " " << (x >> 32) << endl;

与える:0 1

14
R2B2

C++の目標の1つは、「ハードウェアに近い」高速で効率的なコードを可能にすることです。また、ほとんどのハードウェアでは、整数の右シフトまたは左シフトを単一のオペコードで実装できます。問題は、この場合、シフトの大きさがビット数よりも大きい場合、CPUによって動作が異なることです。

したがって、C++がシフト操作の特定の動作を義務付けている場合、オペコードの動作がすべての標準要件に一致しないCPUのコードを生成するとき、コンパイラーはチェックとロジックを挿入して、結果がすべての標準で定義されているとおりであることを確認する必要があります。ケース。オプティマイザがコーナーケースが実際には起こらないことを証明できない限り、これは組み込みシフト演算子のほとんどすべての使用で発生する必要があります。追加されたチェックとロジックにより、プログラムが遅くなる可能性があります。

31
aschepler

特定の例を示すと、x86はシフトカウントを5ビット(64ビットシフトの場合は6ビット)にトリミングし、ARMはシフトカウントを8ビットにトリミングします。現在のC++標準では、コンパイラ両方のCPUが単一のオペコードでシフトを実装できます。

C++標準がシフトの結果を特定の方法でオペランド長を超えて定義する場合、C++が必要とする結果がどちらのハードウェア実装にも一致しない場合、少なくとも1つのCPUファミリ(およびおそらく両方)をターゲットとするコンパイラーあなたが提案する動作のように)、CPUのオペコードがしない必要な結果を生成するブランチを使用して各シフト操作を実装する必要があります。

1