C では、シフト演算子 ( <<
、>>
) は算術演算ですか、それとも論理演算ですか?
11 に答える
左にシフトする場合、算術シフトと論理シフトに違いはありません。右にシフトする場合、シフトのタイプは、シフトされる値のタイプによって異なります。
(違いに不慣れな読者のための背景として、1 ビットの「論理的」右シフトは、すべてのビットを右にシフトし、左端のビットを 0 で埋めます。「算術」シフトは、元の値を左端のビットに残します。 . 負の数を扱う場合、違いが重要になります。)
符号なしの値をシフトする場合、C の >> 演算子は論理シフトです。符号付きの値をシフトする場合、>> 演算子は算術シフトです。
たとえば、32 ビット マシンを想定すると、次のようになります。
signed int x1 = 5;
assert((x1 >> 1) == 2);
signed int x2 = -5;
assert((x2 >> 1) == -3);
unsigned int x3 = (unsigned int)-5;
assert((x3 >> 1) == 0x7FFFFFFD);
あなたが得るシフトのタイプに関して、重要なことはあなたがシフトしている値のタイプです。バグの典型的な原因は、リテラルをシフトして、たとえばビットをマスクオフする場合です。たとえば、符号なし整数の左端のビットを削除したい場合は、これをマスクとして試してみてください。
~0 >> 1
残念ながら、シフトされる値(〜0)が符号付きであるため、マスクのすべてのビットが設定されるため、これは問題になります。したがって、算術シフトが実行されます。代わりに、値を符号なしとして明示的に宣言することによって、つまり次のようにすることによって、論理シフトを強制する必要があります。
~0U >> 1;
C で int の論理右シフトと算術右シフトを保証する関数を次に示します。
int logicalRightShift(int x, int n) {
return (unsigned)x >> n;
}
int arithmeticRightShift(int x, int n) {
if (x < 0 && n > 0)
return x >> n | ~(~0U >> n);
else
return x >> n;
}
あなたがするとき - 1で左にシフトすると2で乗算されます - 1で右にシフトすると2で除算されます
x = 5
x >> 1
x = 2 ( x=5/2)
x = 5
x << 1
x = 10 (x=5*2)
ウィキペディアで調べたところ、次のように書かれていました。
ただし、C には右シフト演算子 >> が 1 つしかありません。多くの C コンパイラは、シフトされる整数の型に応じて、実行する右シフトを選択します。多くの場合、符号付き整数は算術シフトを使用してシフトされ、符号なし整数は論理シフトを使用してシフトされます。
したがって、コンパイラに依存しているようです。また、その記事では、左シフトは算術と論理で同じであることに注意してください。境界ケース (もちろん上位ビット セット) でいくつかの符号付きおよび符号なしの数値を使用して簡単なテストを行い、コンパイラでの結果を確認することをお勧めします。また、C には標準がないように思われるため、少なくともそのような依存を回避することが合理的で可能である場合は、どちらか一方に依存することを避けることをお勧めします。