シフト演算子
C言語のシフト演算子について解説します。シフト演算は、ビット位置を右あるいは、左に移動させる演算のことです。
C言語仕様上では、定義されていませんが、デファクトスタンダードとして、論理シフト演算は、符号なし整数に対して、算術シフト演算は、符号あり整数に対して実装されていると想定してください。VC++やgccはどちらもこの動作をします。
ただし、これは処理系依存ですので、動かない処理系がある可能性はありますが、このように実装しない限り、C言語で論理シフト演算と算術シフト演算を定義できず、移植性のあるライブラリを書けないので、時間がたつにつれて、デファクトスタンダード側の実装で、そろうと想像します。
このような前提に立ちつつ、解説をします。
左論理シフト演算
左論理シフト演算は、指定した回数、左に論理シフトさせます。xは、符号なし整数です。
// 左論理シフト演算 x << count
値が符号なし8bit整数であるとします。これを2bit左へシフト演算すると、次のようになります。8bitを超えた部分は、切り捨てられます。右側は0で埋められます。
// これを左へ2bitシフトさせると 10101101 // このようになる 10101101 // 8bitを超えた部分は切り捨て、右側は0で埋めます 10110100
左論理シフト演算のサンプルプログラムです。
#include <stdio.h> #include <stdint.h> int main(void) { // 10101101 uint8_t x = 0xAD; // 2bit左論理シフト uint8_t y = x << 2; printf("%X\n", y); }
出力結果です。2進数にすると「10110100」です。
B4
右論理シフト演算
右論理シフト演算は、指定した回数、右に論理シフトさせます。xは、符号なし整数です。
// 右論理シフト演算 x << count
値が符号なし8bit整数であるとします。これを2bit右へシフト演算すると、次のようになります。1bitより下の部分は、切り捨てられます。左側は0で埋められます。
// これを右へ2bitシフトさせると 10101101 // このようになる 10101101 // 1bitより下の部分は切り捨て、左側は0で埋めます 00101011
右論理シフト演算のサンプルプログラムです。
#include <stdio.h> #include <stdint.h> int main(void) { // 10101101 uint8_t x = 0xAD; // 2bit右論理シフト uint8_t y = x >> 2; printf("%X\n", y); }
出力結果です。2進数にすると「00101011」です。
2B
左算術シフト演算
左算術シフト演算は、指定した回数、左に算術シフトさせます。xは、符号あり整数です。左算術シフトは、左論理シフトと全く同じ演算です。
// 左算術シフト演算 x << count
値が符号あり8bit整数であるとします。これを2bit左へシフト演算すると、次のようになります。8bitを超えた部分は、切り捨てられます。右側は0で埋められます。
// これを左へ2bitシフトさせると 10101101 // このようになる 10101101 // 8bitを超えた部分は切り捨て、右側は0で埋めます 10110100
左算術シフト演算のサンプルプログラムです。
#include <stdio.h> #include <stdint.h> int main(void) { // 10101101 int8_t x = 0xAD; // 2bit左算術シフト int8_t y = x << 2; // ビットがどうなったかを見たいので符号なしで出力 printf("%X\n", (uint8_t)y); }
出力結果です。2進数にすると「10110100」です。
B4
右算術シフト演算
右算術シフト演算は、指定した回数、右に算術シフトさせます。xは、符号あり整数です。
// 右算術シフト演算 x << count
値が符号あり8bit整数であるとします。これを2bit右へシフト演算すると、次のようになります。1bitより下の部分は、切り捨てられます。左側は符号ビット(一番左のビット。この場合は1)で埋められます。
// これを右へ2bitシフトさせると 10101101 // このようになる 10101101 // 1bitより下の部分は切り捨て、左側は符号ビット(元のビットの一番左のビット。この場合は1)で埋めます 11101011
右算術シフト演算のサンプルプログラムです。
#include <stdio.h> #include <stdint.h> int main(void) { // 10101101 int8_t x = 0xAD; // 2bit右算術シフト int8_t y = x >> 2; // ビットがどうなったかを見たいので符号なしで出力 printf("%X\n", (uint8_t)y); }
出力結果です。2進数にすると「11101011」です。
EB
シフト演算ってどこで使うの?
えーっと、普段は使いません(笑)。アプリケーションを作っている場合は、使わないんじゃないでしょうか。
でも、僕は、エッジケースを知らないので「使わないんじゃないでしょうか」くらいにとどめておきます。
コンパイラが自動的に最適化してくれるので、算術演算よりもシフト演算を使った方が速くなるということは、まずないですね。
これも、僕は、エッジケースを知らないので「まずない」くらいにとどめておきますね。
ライブラリを作成する場合は、疑似乱数を作ったり、MD5やSHAでハッシュ値を生成する関数の場合に、シフト演算を使います。ランダム性をプログラムで表現したい場合の実装に使われているのは、見ますね。暗号化にかかわるライブラリの関数では、目にする機会が多いと思います。