右シフト演算子の興味深い動作
右シフト演算子 (>>) は、大きな右シフト値を処理するときに独特の動作を示します。次のプログラムを考えてみましょう:
#include
#include
int foo(int a, int b)
{
return a >> b;
}
int bar(uint64_t a, int b)
{
return a >> b;
}
int main()
{
std::cout > 32: " > 32) > (int)32: " > (int)32) foo(1, 32) の予想される出力は 0 ですが、驚くべきことに 1 が返されます。これは次のことに起因すると考えられます:
論理シフトと算術演算Shift
x86/x86-64 アーキテクチャでは、右シフト演算子は実際には 論理右シフト を実行します。つまり、左オペランドの符号。この動作は、>>> b.
Compiler Optimization
を使用する場合と似ています。foo(1, 32) の場合、値 32 がキャストされます。 int は実質的に 32 ビットに切り捨てられます。 int が保持できる最大値は 231-1 であるため、右シフトは本質的に >>> (32 % 32) となり、0.
Unknown と評価されます。動作
関連する C 標準では、プロモートされた左オペランドの幅以上のカウントを持つ右シフトの「動作は未定義」であると規定されています。この場合、1 >> 32 と (int)1 >> (int)32 の両方のカウントが 32 より大きく、予測できない結果が生じます。
bar(1, 32) との違い
関数 bar は 64 ビットの符号なし整数を受け取り、幅が 32 より大きいことが保証されています。したがって、bar の右シフトは未定義の動作の影響を受けません。
結論
大きなシフト値を扱う場合、右シフト演算子の動作があいまいになります。 x86/x86-64 アーキテクチャでは論理右シフトが実行されますが、ARM では異なる実装が使用される場合があります。未定義の動作のため、オペランドの幅以上のカウントによる右シフトの結果は、移植可能なコードでは回避する必要があります。
免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。
Copyright© 2022 湘ICP备2022001581号-3