右シフト演算子の興味深い動作
右シフト演算子 (>>) は、大きな右シフト値を処理するときに独特の動作を示します。 。次のプログラムを考えてみましょう:
<code class="c++">#include <iostream> #include <stdint.h> int foo(int a, int b) { return a >> b; } int bar(uint64_t a, int b) { return a >> b; } int main() { std::cout << "foo(1, 32): " << foo(1, 32) << std::endl; std::cout << "bar(1, 32): " << bar(1, 32) << std::endl; std::cout << "1 >> 32: " << (1 >> 32) << std::endl; //warning std::cout << "(int)1 >> (int)32: " << ((int)1 >> (int)32) << std::endl; //warning }
foo(1, 32) の期待される出力は 0 ですが、驚くべきことに 1 が返されます。これは次のことが原因である可能性があります:
論理シフトと算術シフト
x86/x86-64 アーキテクチャでは、右シフト演算子は実際に 論理右シフト を実行します。これは、空いた領域を埋めることを意味します。左オペランドの符号に関係なく、ビットは 0 になります。この動作は、>>> を使用するのと似ています。 b.
コンパイラの最適化
foo(1, 32) の場合、値 32 は int にキャストされ、事実上 32 ビットに切り捨てられます。 int が保持できる最大値は 231-1 であるため、右シフトは本質的には >>> です。 (32 % 32)、0 と評価されます。
未定義の動作
関連する C 標準では、カウントが大きい右シフトについては「動作は未定義」であると述べられています。プロモートされた左オペランドの幅以上。この場合、両方とも 1 >> 32 と (int)1 >> (int)32 のカウントは 32 より大きく、予測できない結果が発生します。
bar(1, 32) との違い
関数 bar は 64 ビットを使用します。符号なし整数。幅が 32 より大きいことが保証されています。したがって、バーの右シフトは未定義の動作の影響を受けません。
結論
の動作大きなシフト値を扱う場合、右シフト演算子があいまいになります。 x86/x86-64 アーキテクチャでは論理右シフトが実行されますが、ARM では異なる実装が使用される場合があります。未定義の動作のため、オペランドの幅以上のカウントによる右シフトの結果は、移植可能なコードでは回避する必要があります。
以上が## 右シフト演算子が大きなシフト値で予期しない動作を示すのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。