[C言語入門] 演算子の使い方(10)


高木です。こんにちは。

前回、やっとのことで二項算術演算子が一通り終わりました。
分量でいえば、やっと半分ぐらいでしょうか?
まだまだ演算子があるのですが、別にこれはCだから多いのではなく、もっと簡単といわれている言語のほうがたくさんあると思いますよ。

今回はビット単位のシフト演算子の解説です。
昔はC特有の演算子のようないわれかたをしていた時代もあります。
現在では、Cの流れを汲む言語ならどれでも(微妙な違いはあるにせよ)ビットラン単位のシフト演算子を持っています。

ビット単位のシフト演算子

具体的な解説に入る前に、ビット単位のシフトというのが何かについて触れておきましょう。

コンピュータは0と1だけで情報を表すといった話を聞いたことがあると思います。
また、コンピュータでは2進法を使うという話もありますね。

実際そうで、コンピュータでは0と1の2つの値を表現できる「ビット」をいくつも集めてデータを表現します。
たとえば、整数を表現する場合を考えてみましょう。
0と1だけの「ビット」を4つ集めたとします。
そうすると、\(2^4=16\)通りの値を表現することができます。

ビットの並びが0000なら0を、0001なら1を、0010なら2を、0011なら3を、それぞれ表すことになります。
ここではわかりやすく桁を小さくしましたが、ビットの並びが8個であれば8ビット、16個であれば16ビット、32個であれば32ビットの値を表現することができます。

ビット単位のシフトというのは、このビットの並びを左または右にずらすことを意味します。
左に1つずらせば、それは桁があふれない限りは2倍することを意味します。
右に1つずらせば、値を半分にすることを意味します。

このことを踏まえて、ビット単位のシフト演算子を見ていきましょう。

ビット単位のシフト演算子は、左シフトがE1 << E2、右シフトがE1 >> E2のように、不等号を2つ続けて記述します。

ビット単位のシフト演算子のオペランドは両方とも整数でなければなりません。
式を評価する前には、左右のオペランドを「整数拡張」します。
常、二項演算子は左右のオペランドを「通常の算術型変換」しますが、ビット単位のシフト演算子は「整数拡張」するので注意してください。
評価結果の型は、左オペランドを「整数拡張」した結果の型になります。

左シフトでは、右オペランドが負の場合、あるいは整数拡張された左オペランドのビット幅以上の場合、未定義の動作になります。
左オペランドを整数拡張した結果が32ビットの場合、右オペランドは0~31の範囲でなければならないことを意味しています。
32でも未定義の動作になります。

また、左オペランドが負の場合、右オペランドの値に関わらず未定義の動作になります。
たとえ、右オペランドの値が0でも未定義の動作になります。

基本的に左シフトE1 << E2の結果の値はE1×2E2になります。
しかし、結果の値を表現できない場合は注意が必要です。

左オペランドを整数拡張した結果の型が符合無し整数型を左シフトした場合、結果の値が表現できなければ、結果の型の最大値+1を法とする剰余が評価結果になります。
左オペランドを整数拡張した結果の型が符合付き整数型を左シフトした場合、結果の値が表現できなければ未定義の動作になります。
左オペランドがunsigned char型やunsigned short型のような符合無し整数型であっても、整数拡張の結果、符合付き整数型であるint型になる場合があるので要注意です。

右シフトE1 >> E2の結果の値は、基本的にE1÷2E2になります。
ただし、端数がある場合は切り捨てになります。

左オペランドが負の値を持つ場合、右シフトした結果は処理系定義になります。
これに関して、「算術シフト」になるか「論理シフト」になるかが処理系定義になるといわれる場合があります。
確かにそういうこともあるかもしれませんが、おそらく想定しているのはそういうことではないと思います。

Cでは、負の整数値を表現する方法として、2の補数のほか、1の補数や符合ビットと絶対とがあります。
それらの詳細については割愛しますので、興味のある方は独自に調べてみてください。

たとえば、負の整数値の表現に1の補数を使う場合を考えてみましょう。
int型が16ビットの場合、-1は1111 1111 1111 1110になります。
これを1ビット右の算術シフトすると1111 1111 1111 1111となり、これは-0またはトラップ表現になります。

一方、2の補数表現を使う場合の-1は1111 1111 1111 1111になります。
これを1ビット右の算術シフトすると、やはり1111 1111 1111 1111となり、これは-1を表します。
つまり、これが処理系定義だということです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください