演算子―プログラムにおける基本的な構成要素:目指せ! Cプログラマ(4)(2/3 ページ)
プログラミング言語の基本となる「C」。正しい文法や作法を身に付けよう。Cには確かに学ぶだけの価値がある(編集部)
シフト演算子
シフト演算子はビット単位のシフトを行います。演算子の左にはシフトする対象を、右にはシフト量を書きます。
ビット単位のシフトとは、2進数で表した値の各けたを左右にずらすことをいいます。空いた場所には0が入り、はみ出たビットは捨てられます。
例えば、10進数の5(2進数の101)は、1ビット分だけ左にシフトすると、10進数の10(2進数の1010)になります。
シフト演算にはいくつか制限があります。
- シフト量として負の値を指定してはいけません
- シフト量としてシフト対象のビット幅を超えた値を指定してはいけません
- 負の値を左シフトしてはいけません
- 負の値を右シフトしたときの動作はコンパイラによって異なります
また、符号付き整数型を左シフトしたときには、その型で表現できなくなる可能性があります。その場合にも予期しない動きをするかもしれませんので、シフト演算の左オペランドには、理由のない限り符号なし整数型を使った方がよいでしょう。
プログラムの実行速度が要求されるときに、シフトは2の整数倍を乗算する計算として扱われることがあります。一般的に、1ビットの左シフトは2倍することと同じ結果になり、1ビットの右シフトは2分の1倍(つまり2の−1乗倍)と同じ結果になります。
たいていのコンピュータにおいてビット単位のシフトは、乗算や除算よりも高速に行われるため、2倍や、2分の1倍の演算を高速に行うためにビット単位のシフトを利用することがあります。特に、性能が限られている組み込みソフトウェアなどで、少しでも速く処理させたいときに利用することがあります。
ただし、前述の制限を考慮すると、シフト対象の値やシフト量によっては正確に2倍したり2分の1倍した結果とは異なることがありますので、注意が必要です。特に理由がない限りは、*演算子や/演算子を使った方が良いでしょう。
どうしてもそのような用途で利用する必要があるときは、予想外の動作を引き起こさないように、注意して記述してください。
printf("4 << 1 : %d\n", 4 << 1); printf("4 >> 1 : %d\n", 4 >> 1);
4 << 1 : 8 4 >> 1 : 2
AND演算子、排他OR演算子、OR演算子、補数演算子
AND演算子(&)、排他OR演算子(^)、OR演算子(|)は、左右のオペランドにおけるビット単位の論理積、排他論理和、論理和を求めます。補数演算子(~)は、右オペランドのみをとり、そのオペランドの補数を求めます。
- 論理積(AND)は、両方のビットがセットされていれば、結果のビットがセットされます
- 排他論理和(XOR)は、いずれか一方のビットがセットされていれば、結果のビットがセットされます
- 論理和(OR)は、少なくとも一方のビットがセットされていれば、結果のビットがセットされます
- 補数は、ビットが反転(セットされていないときだけセット)されます。
AND(&) | XOR(^) | OR(|) | ||
---|---|---|---|---|
0 ( ) 0 | 0 | 0 | 0 | |
0 ( ) 1 | 0 | 1 | 1 | |
1 ( ) 0 | 0 | 1 | 1 | |
1 ( ) 1 | 1 | 0 | 1 |
補数(~) | ||
---|---|---|
~0 | 1 | |
~1 | 0 |
論理積、排他論理和、論理和、補数の演算は、フラグの処理に使われることがあります。フラグとは、処理の判断基準として使われる値のことをいいます。
このフラグがセットされているかどうかで処理を行ったり、行わなかったり、あるいは別の処理を行ったりします。論理演算を使うと任意のビットをセットしたり、0にしたり、あるいは反転させたりすることができますので、これをフラグの処理に利用するわけです。
- あるビットをセットしたいときは、そのビットのみセットされた値との論理和(OR)を計算する
- あるビットを反転させたいときは、そのビットのみセットされた値との排他的論理和(XOR)を計算する
- あるビットをセットされていない状態にしたいときは、そのビットのみセットされていない値との論理積(AND)を計算する
- あるビットのみセットされていない値を求めるには、そのビットのみセットされた値の補数(~)を計算する
これらを組み合わせると、さまざまなビット操作が実現できます。前述の表を見ながら動きを確認してみてください。
なお、これらの演算子はオペランドに整数のみが指定できます。また、これらの演算子はオペランドに整数拡張を行ってから演算を行いますので、char型に収まるような小さな値を使っていてもint型に拡張されます。整数拡張については次回説明します。
printf("x y : x&y x^y x|y \n"); printf("0 0 : %d %d %d\n", 0&0, 0^0, 0|0); printf("0 1 : %d %d %d\n", 0&1, 0^1, 0|1); printf("1 0 : %d %d %d\n", 1&0, 1^0, 1|0); printf("1 1 : %d %d %d\n", 1&1, 1^1, 1|1); printf("\n"); printf("~0&1 : %u\n", ~0&1); printf("~1&1 : %u\n", ~1&1);
x y : x&y x^y x|y 0 0 : 0 0 0 0 1 : 0 1 1 1 0 : 0 1 1 1 1 : 1 0 1 ~0&1 : 1 ~1&1 : 0
このコード片では、これまで出てこなかったprintfの使い方をしています。例えば、「printf("0 0 : %d %d %d\n", 0&0, 0^0, 0|0);」では、「0&0」の結果が最初の「%d」、「0^0」の結果が2番目の「%d」、「0|0」の結果が3番目の「%d」の位置に入ります。詳細は別の回で説明しますので、ここではprintfはこんな便利な使い方ができることだけ覚えておいてください。
最後の2行では、符号なし整数型の値として表示するために「%u」を使っています。また、1ビット目(2進数で書いたときのいちばん右のけた)の値を取り出すため、「&1」を付けている点に注意をしてください。
これを付けない「printf("~0 : %u\n", ~0);」を実行すると、筆者の環境では「~0 : 4294967295」という結果が出ました。単に0と書くとint型になりますので、Pleiadesの環境では32ビットの符号付き整数値になります。そのすべてのビットが反転されるわけですから、2進数では「1111...(略)...11」、つまり4294967295(= 0xFFFFFFFF)となるのです。
Copyright © ITmedia, Inc. All Rights Reserved.