C で
>`) を 32 ビットシフトすると予期しない結果が発生しますか?
" />
右シフト演算子の予期しない動作 (1 >> 32)
プログラミングの分野では、右シフト演算子 (>>) がよく使用されますビット単位の演算、特に整数を 2 の累乗で除算する場合は、次の C コードで示されているように、より大きな値でシフトするときに特有の動作が発生する可能性があります。 , int b) { a >> b を返します。 } int bar(uint64_ta, int b) { a >> b を返します。 } int main() { std::cout > 32: " > 32) > (int)32: " > (int)32)
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): 1 // 0 である必要があります。
バー(1, 32): 0
1>>32:0
(int)1 >> (int)32: 0これらの結果の背後にある理論的根拠は、CPU とコンパイラの内部動作にあります。foo(1, 32): 1 // Should be 0
bar(1, 32): 0
1 >> 32: 0
(int)1 >> (int)32: 0
foo() 関数の動作
foo() 関数では、シフト操作がキャストなしで実行され、CPU が論理右シフトを実行します。多くのアーキテクチャでは、論理右シフトは >> (b % 32) として実装され、b の上位ビットは事実上無視されます。したがって、 foo(1, 32) の結果は 1 >> (32 % 32) となり、これは 1 >> 0 と評価され、1 が得られます。
64 ビット整数へのキャストはなぜ重要ですか?
bar() 関数では、64 ビットの符号なし整数が提供され、b (32) がオペランドのビット数 (64) より小さいため、結果が 0 になることが保証されます。 )。ただし、b が 64 に変更されると、結果は予測不能になり、依然として 1 が生成される可能性があります。
コンパイラの最適化
1 >> 32 および (int の場合) )1 >> (int)32 の場合、コンパイラはコンパイル時にこれらの定数式を最適化します。標準では、カウントが負の場合、またはオペランドの長さ以上の場合の右シフトの未定義の動作が指定されています。 32 はオペランドの長さを超えているため、コンパイラは結果を判断できず、安全なフォールバックとして 0 を出力します。
CPU 固有の動作
右シフトの実装動作は CPU ごとに異なる場合があります。 x86/x86-64 アーキテクチャでは、モードに応じて、論理右シフトは実質的に >> (b % 32 または 64) になります。ただし、ARM プロセッサでは、右シフト演算は 32 以上のシフトに対してゼロを保証します。
結論
右シフト演算子を使用する場合、特にシフト数がオペランドの長さを超える場合、潜在的な未定義の動作を考慮します。 64 ビット整数などのより広い整数型にキャストすると、さまざまな CPU やコンパイラ間で一貫した結果を保証できます。
免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。
Copyright© 2022 湘ICP备2022001581号-3