
C言語基礎|シフト演算
ビットをずらすだけで、掛け算・割り算みたいに動く!シフト演算で“2進数の感覚”を身につけよう。
シフト演算ってなに?
シフト演算は、整数の中に並んでいる 0 と 1(ビット列)を、左または右にまとめて「ずらす」操作です。
C言語では、次の2つが主役になります。
- 左シフト:<<
- 右シフト:>>
ビットをずらすと、2進数の桁が動くので、(条件がそろえば)値が 2倍 / 1/2 みたいに変化します。
ただし、オーバーフローや符号付き負数の扱いなど、落とし穴もあるので、そこも一緒に丁寧に押さえていきましょう。
シフト演算子(<< と >>)の基本
ビット単位のシフト演算子
| 演算子 | 書式 | 何をする? | 空いたビット |
|---|---|---|---|
| << | a << b | a を b ビット左にずらす | 右側(下位側)に 0 を入れる |
| >> | a >> b | a を b ビット右にずらす | 左側(上位側)がどう埋まるかは状況次第 |
この表の説明
- << は「左へ移動して、空いたところは 0 で埋める」と覚えてOKです(非負の範囲で特に分かりやすい)。
- は、unsigned(符号無し)なら左側は 0 が入るのが基本イメージで、非負なら割り算っぽく動きます。
- どちらも 整数型にしか使えません(double などはエラー)。
シフトすると何が起きる?
本文の Fig.7-17 を、見やすい図として整理します(例は非負の整数)。
図:左シフトと右シフト(非負の例)

この図の説明
- 左シフトは「右側に 0 を足す」感じになり、値が大きくなりやすいです。
- 右シフトは「下位ビットが落ちる」ので、値が小さくなりやすいです。
- ただし、左に押し出されたビットは消えるので、情報が失われます(これがオーバーフローや桁あふれの感覚につながります)。
値としてはどう変化する?(非負・unsignedでの目安)
非負の整数でのシフトと値の関係(目安)
| 式 | ざっくりした意味 | 条件 |
|---|---|---|
| x << n | x × 2n | オーバーフローしない範囲で |
| x >> n | x ÷ 2n の整数部分 | x が非負、特に unsigned だと分かりやすい |
この表の説明
2進数は各桁が 20, 21, 22… の重みを持っています。
だから、1ビット左にずらすと重みが1段上がって 2倍、右にずらすと 1/2 っぽくなる、というわけです。
重要:符号付きの負数をシフトしないほうがいい理由
本文にもある通り、負数の右シフトの結果は処理系に依存しやすいです。
多くの処理系では次のどちらかになります。
右シフトの2つの考え方(代表例)
| 名前 | 何が起きる? | 目的 |
|---|---|---|
| 論理シフト | 左側を 0 で埋める | ビット列として扱いたい |
| 算術シフト | 左側を符号ビットで埋める | 符号付きの割り算っぽくしたい |
この表の説明
- 同じ x >> n でも、負数だと結果が変わる可能性があり、移植性が下がります。
- なので、教材としては 負数のシフトはやらない方針でOKです。
- ビット操作をしたいときは、まず unsigned を使うのが安全です。
登場する命令(演算子)の書式と「何をするか」
シフト演算子
| 演算子 | 書式 | 何をする命令?(役割) |
|---|---|---|
| << | a << b | a の全ビットを b だけ左に移動して新しい値を作る。 |
| >> | a >> b | a の全ビットを b だけ右に移動して新しい値を作る。 |
代入を伴うシフト(よく使う書き方)
本文には直接は出ていませんが、同系列としてセットで覚えると便利です。
| 演算子 | 書式 | 意味 |
|---|---|---|
| <<= | a <<= b | a = a << b と同じ |
| >>= | a >>= b | a = a >> b と同じ |
この表の説明
- 繰り返しシフトする処理(ビット走査、分解、圧縮など)で大活躍します。
サンプルプログラム
2の累乗マスクを作ってビット位置を示すプログラムです。
- 入力:ビット位置 pos(0〜)
- 出力:1U << pos の値とビット列
- ついでに右シフトで元に戻す様子も見せます
プロジェクト名:chap7-17-1 ソースファイル名:chap7-17-1.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
// シフト演算で「ビットマスク」を作って確認する例
#include <stdio.h>
int count_bits(unsigned x)
{
int bits = 0;
while (x) {
bits++;
x >>= 1;
}
return bits;
}
int int_bits(void)
{
return count_bits(~0U); // unsigned の全ビットが1の値を数える
}
void print_bits(unsigned x)
{
for (int i = int_bits() - 1; i >= 0; i--) {
putchar(((x >> i) & 1U) ? '1' : '0');
}
}
int main(void)
{
unsigned pos;
printf("ビット位置を入力してください(0以上)。\n");
printf("pos = ");
scanf("%u", &pos);
unsigned mask = 1U << pos;
putchar('\n');
printf("作ったマスク値 = %u\n", mask);
printf("マスクのビット列 = ");
print_bits(mask);
putchar('\n');
printf("右に戻す(mask >> pos)= ");
print_bits(mask >> pos);
putchar('\n');
printf("\nメモ:1U を pos だけ左にずらすと、その位置だけ1になります。\n");
return 0;
}このプログラムで学べること(説明)
- 1U << pos は、pos番目のビットだけが1の値(マスク)を作る定番テクニックです。
- mask >> pos をすると、(posが範囲内なら)最下位ビットに 1 が降りてきて、結果は 1 になりやすいです。
- print_bits の中では x >> i を使って i番目のビットを取り出すことをしています。
よくある注意点(超大事)
シフト演算の注意ポイント
| 注意点 | 何が起きる? | 対策 |
|---|---|---|
| シフト量が大きすぎる | ビット幅以上のシフトは危険(未定義になり得る) | 0〜(ビット数-1)に収める |
| 左シフトのオーバーフロー | はみ出したビットが消える | 範囲内で使う/unsigned中心で扱う |
| 負数の右シフト | 結果が処理系依存になりやすい | 負数はシフトしない、unsignedを使う |
この表の説明
「動くはず」という感覚で書くと危ない場所なので、学習段階から安全なルールで身につけるのがおすすめです。
まとめ:シフトは“ビットの引っ越し”
- << は左へ引っ越し(右に 0 が入る、値は増えやすい)
- は右へ引っ越し(下位が落ちる、値は減りやすい)
- ビット操作は unsigned を基本にすると安全
- 1U << pos はマスク作りの定番
