2進数は,各桁を0か1かで表す表記法であり,その各桁をビットとも呼ぶ. 最も右側のビット(最下位ビット)の番号を第0ビットとして, 左に行くに従い,第1ビット,第2ビット,...と呼ぶ.第 n ビットが表す数値は 2n である.
一番小さいサイズの型である char 型のサイズは 1バイト = 8ビット であるので, 8桁の2進数の各桁の表す数値を表で見てみよう.
| ビット番号 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 
| 表す数値 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | 
たとえば, 77 を表す2進数は 01001101(2) である.
| 77 = 64 + 8 + 4 + 1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 
char 型を構成する 8 ビットのうち, 最上位,すなわちもっとも左側のビットを MSB (most significant bit) といい, 最下位,すなわちもっとも右側のビットを LSM (least significant bit) という.
4ビットの2進数が表す数値は 0 = 0000(2) から 15 = 1111(2) である.この数値を 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f で表すと, 4ビットを一文字で表すことができ, すなわち1バイト(=8ビット)を2文字で表すことができる. これが16進数表示である.
16進数の第 n 桁(右端を第 0 桁とする)が表す数値は 16n である.
先ほどの 77 の場合は, 77 = 01001101(2) を4桁ごとに区切ると, 0100(2) = 4, 1101(2) = d であるから, 77 = 4d(16) となる.
| 77 = 64 + 8 + 4 + 1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 
| 77 = 4*16 + 13*1 | 4 | d (=13) | ||||||
現在のコンピュータは4バイトをひとまとまりとして扱うことが多い (例えば int 型は4バイト). 4バイトは 4*8 = 32 ビットであるから,これを2進数で表すと 32 桁にもなる. しかし16進数だと,4ビットを1文字で表すので8文字で表せることになる.
例えば, 7777777 = 00000000011101101010110111110001(2) = 0076adf1(16) である.
| 2進数 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 
| 16進数 | 0 | 0 | 7 | 6 | a | d | f | 1 | ||||||||||||||||||||||||
Cのプログラム中で,整数の定数を16進数で表すことができる. その定数には16進数であることの印のために,頭に 0x を付けることになっている. ( x は16進数 hexadecimal number の x を意味する)
次のプログラムで確かめてみよう.(書式文字列 %x は16進数で整数を表示する指定.)
#include <stdio.h>
int main()
{
  char c;
  int i;
  c = 0x4d;
  i = 0x0076adf1;
  printf("%x = %d\n%x = %d\n", c, c, i, i);
  
  return 0;
}
4d = 77 76adf1 = 7777777
C言語では,char 型や int 型などの整数を表す型には, 符号付き(signed)の型と符号無し(unsigned)の型とがある. 符号付きの型は負の数も表すことができるが,符号無しの型は0以上の整数しか表せない. しかし,符号付きの型が表すことができる最大の整数は,符号無しの型が表すことができる 最大の整数の約半分となる.
char や int などの整数を表す型名に,キーワード unsigned を付けると,それは符号無しの型となる.
    unsigned char c;  /* 符号無し char 型 */
    unsigned int i;   /* 符号無し int 型 */
この場合,表すことのできる最大の整数値は,変数のもつビット数を n とするとき,2n-1 となる.
0 ≦ unsigned char 型の値 ≦ 255 = 28-1
0 ≦ unsigned int 型の値 ≦ 4294967295 = 232-1
char や int などの整数を表す型名に,キーワード signed を付けると,それは符号付きの型となる.
    signed char c;  /* 符号付き char 型 */
    signed int i;   /* 符号付き int 型 */
この場合,変数のもつビット数を n とするとき,全部で 2n 通りの値を表せるが, それを負の数と正の数とに半々に割り振るので,表すことのできる値の範囲は 最小 -2n-1 から最大 2n-1-1 となる.
-27 = -128 ≦ char型の値 ≦ 127 = 27-1
-231 = -2147483648 ≦ int型の値 ≦ 2147483647 = 231-1
符号無し,符号付きを指定せずに,単に char や int で宣言した場合も, 符号付きの型となる.
    char c;  /* 符号付き char 型 */
    int i;   /* 符号付き int 型 */
じつは,単に char と宣言したときに符号付きになるかどうかは, 処理系依存であるが,符号付きになることが普通である. どちらであるかは, limits.h の中で定義されている CHAR_MAX の値を見ればわかる (次のカラム参照).
ヘッダファイル limit.h には,各種の整数型が表すことのできる数値の最大値と最小値とが, マクロ定数として定義されている.
#include <stdio.h>
#include <limits.h>
int main()
{
  printf("CHAR_MIN  = %4d,      CHAR_MAX  = %4d\n", CHAR_MIN, CHAR_MAX);
  printf("SCHAR_MIN = %4d,      SCHAR_MAX = %4d\n", SCHAR_MIN, SCHAR_MAX);
  printf("UCHAR_MAX = %4d\n", UCHAR_MAX);
  printf("INT_MIN   = %11d,     INT_MAX = %11d\n", INT_MIN, INT_MAX);
  printf("UINT_MAX  = %11u\n", UINT_MAX);
}
CHAR_MIN = -128, CHAR_MAX = 127 SCHAR_MIN = -128, SCHAR_MAX = 127 UCHAR_MAX = 255 INT_MIN = -2147483648, INT_MAX = 2147483647 UINT_MAX = 4294967295
コンピュータの内部で整数値がどのようなビット状態で表されるかを見る. 簡単のために unsigned char と signed char とを例にとって説明するが, unsigned int と signed int とについても同様である.
C言語では,残念ながら整数値を2進数で表示する簡単な方法がない. (16進数なら先ほどのように printf の書式文字列に "%x" で指定すればできた.) そこで,次のような関数を自作して表示させることにする. 関数の内容はまだ理解できなくてもよいので,とりあえずキーボードから打ち込むか, コピー&ペーストして,実行するように.
#include <stdio.h>
/* 符号無し char 型の値と,そのビットの状態を表示する */
void show_unsigned_char(unsigned char x)
{
  int i;
  printf("%4u  %02x  ", x, x); 
 
  for (i = 7; i >= 0; i--) {    
    printf("%d", (x>>i) & 1); 
  }
  printf("\n");
}
int main()
{
  show_unsigned_char(-2);
  show_unsigned_char(-1);
  show_unsigned_char(0);
  show_unsigned_char(1);
  show_unsigned_char(2);
  show_unsigned_char(3);
  show_unsigned_char(127);
  show_unsigned_char(128);
  show_unsigned_char(129);
  show_unsigned_char(254);
  show_unsigned_char(255);
  show_unsigned_char(256);
  show_unsigned_char(257);
  
  return 0;
}
コンパイルすると,次のような警告が出るが,一応コンパイルでき,実行できる.
u_char_bit.c:27: 警告: large integer implicitly truncated to unsigned type u_char_bit.c:28: 警告: large integer implicitly truncated to unsigned type
実行結果は次のようになる.左から,10進数,16進数,そしてビットの状態である. 符号無し整数値を表すビットの状態は,通常の2進数表記と同一であることに注意しよう.
254 fe 11111110 255 ff 11111111 0 00 00000000 1 01 00000001 2 02 00000010 3 03 00000011 127 7f 01111111 128 80 10000000 129 81 10000001 254 fe 11111110 255 ff 11111111 0 00 00000000 1 01 00000001
ところで,-2, -1 という負の数を入れたはずなのに,実際には 254, 255 という数値となり, 256, 257 という数値に対しては,0, 1 となっていることにも注意しよう. これは, -2, -1, 256, 257 という定数値が unsigned char が表すことのできる範囲を超えているからである. 先ほどの警告は,このことの警告であった. (定数が範囲を超えているときには,コンパイル時にこのような警告が出ることもあるが, 実行時に変数値が範囲を超えても,警告もエラーも出ない.)
このように, 符号無し char 型の値が,それが表すことのできる範囲を超えてしまった場合, 0, 1, 2, ..., 254, 255 の繰り返しとなる.
符号無し int についても同様に, 0, 1, 2, ... 4294967295 の繰り返しとなる.
符号付きの整数値のビットは,2の補数表現という状態になっている.それを見てみよう.
#include <stdio.h>
/* 符号無し char 型の値と,そのビットの状態を表示する */
void show_signed_char(signed char x)
{
  int i;
  printf("%4d  %02x  ", x, (unsigned char)x);
  for (i = 7; i >= 0; i--) {
    printf("%d", (x>>i) & 1);
  }
  printf("\n");
}
int main()
{
  show_signed_char(-130);
  show_signed_char(-129);
  show_signed_char(-128);
  show_signed_char(-127);
  show_signed_char(-3);
  show_signed_char(-2);
  show_signed_char(-1);
  show_signed_char(0);
  show_signed_char(1);
  show_signed_char(2);
  show_signed_char(3);
  show_signed_char(126);
  show_signed_char(127);
  show_signed_char(128);
  show_signed_char(129);
  return 0;
}
これもコンパイルすると,警告が出るが,一応コンパイルができて実行もできる.
s_char_bit.c:16: 警告: overflow in implicit constant conversion s_char_bit.c:17: 警告: overflow in implicit constant conversion
実行結果は次のようになる. ここで,数値が負の時には最高位(左端)ビットが 1 であることに注意しよう. このように,符号付きの型の最高位ビットは,その符号を表すので,符号ビットと呼ばれる.
126 7e 01111110 127 7f 01111111 -128 80 10000000 -127 81 10000001 -3 fd 11111101 -2 fe 11111110 -1 ff 11111111 0 00 00000000 1 01 00000001 2 02 00000010 3 03 00000011 126 7e 01111110 127 7f 01111111 -128 80 10000000 -127 81 10000001
ところで,-130, -129 と負の方向に範囲を逸脱すると,その値は 126, 127 となり, 128, 129 と正の方向に範囲を逸脱すると,その値は -128, -127 となることにも注意しよう.
このように,符号付き char 型の値が,それが表すことのできる範囲を超えてしまった場合, -128, -127, ..., 126, 127 の繰り返しとなる.
符号付き int の場合も同様に, -2147483648, ..., 2147483647 の繰り返しとなる.
符号付きの型においては,すべてのビットの0と1との値を入れ替えるビット反転という操作で,正の数と負の数とは次のように対応している.
| 0 | 1 | 2 | 3 | ... | 124 | 125 | 126 | 127 | 
| 00000000 | 00000001 | 00000010 | 00000011 | ... | 01111100 | 01111101 | 01111110 | 01111111 | 
| 11111111 | 11111110 | 11111101 | 11111100 | ... | 10000011 | 10000010 | 10000001 | 10000000 | 
| -1 | -2 | -3 | -4 | ... | -125 | -126 | -127 | -128 | 
普通,正と負とを入れ替えるという操作は,0 を中心にしての反転となるが, 符号付き整数型でビット反転をすると, 0 と -1 の間(すなわち -0.5)を中心としての反転となる. 式で書くと,次の通り.
xをビット反転 = -x-1
整数値を扱うための型には,( signed, unsigned ) char 型と int 型とがあることは説明したが, それら以外にもある.ここで,まとめて簡単に解説する.
| バイト数 | ビット数 | 最小値 | 最大値 | |
|---|---|---|---|---|
| unsigned char | 1 | 8 | 0 | 255 | 
| signed char | 1 | 8 | -128 | 127 | 
| unsigned short int | 2 | 16 | 0 | 65535 | 
| signed short int | 2 | 16 | -32768 | 32767 | 
| unsigned int | 4 | 32 | 0 | 4294967295 | 
| signed int | 4 | 32 | -2147483648 | 2147483647 | 
| unsigned long int | 4 | 32 | 0 | 4294967295 | 
| signed long int | 4 | 32 | -2147483648 | 2147483647 | 
| unsigned long long int | 8 | 64 | 0 | 18446744073709551615 | 
| signed long long int | 8 | 64 | -9223372036854775808 | 9223372036854775807 | 
基本的な整数型には char, short int , int, long int, long long int の4種類があり,そのサイズは char ≦ short int ≦ int ≦ long int ≦ long long int となる.
char 型は 1 バイトであると決まっているが,あとの種類はサイズが厳密に決められたものではなく, 将来は変わる可能性がある. 実際に,数年前のパソコンは 16ビットマシンが多かったので, int 型は 16ビット=2バイトであった.
今使っているコンピュータの環境では,たまたま int 型と long int 型とは,まったく同じものとなっている.
整数型の値を表すビットを,直接操作するための,ビット演算子と呼ばれる種類のものが6個ある. それらは4個のビット論理演算子と2個のシフト演算子に類別される.
ビット論理演算子は,対応する位置のビットごとに操作を施した結果を返す.
| A | B | A & B | 
|---|---|---|
| 0 | 0 | 0 | 
| 0 | 1 | 0 | 
| 1 | 0 | 0 | 
| 1 | 1 | 1 | 
& はビット論理積(AND)演算子である. 二つの整数値 a, b に対して, a & b は対応するビットごとに論理積 (AND) をとった結果を返す.
1ビットの値 A, B に対する A & B の値は右の表の通り.
& 演算子は,いくつかに限定したビットの状態だけを取り出したいときに,よく使われる. たとえば,char 型 8 ビットのうち,下位の 4 ビットだけの状態を取り出すならば, 00001111(2) = 15 = 0f(16) とのビット論理積をとればよい. 次の例では, x = 0x6a = 01101010(2) の下位4ビットの値 0x0a = 10 = 00001010(2) の値を取り出している.
int main()
{
  unsigned char x, y, z;
  x = 0x6a;
  y = 0x0f;
  z = x & y;
  show_unsigned_char(x);
  show_unsigned_char(y);
  show_unsigned_char(z);
  
  return 0;
}
106 6a 01101010 15 0f 00001111 10 0a 00001010
また, x = x & y の代わりに x &= y と書くこともできる.
x &= y; /* x = x&y と同じ */
次の関数は引数 x を 2 で割ったときの余りを返す.
int remainder2(int x)
{
  return x & 1;
}
1 は 0000...0001 であるから,x & 1 は x の最下位ビットの値に等しい.それが x を 2 で割ったときの余りである.
引数 x を 8 で割ったときの余りを返す関数 remainder8(x) を, & 演算子を用いて作れ.
int remainder8(int x)
{
    ....
}
ヒント: 8 で割ったときの余りは,下位3ビットが表す値に等しい.
| A | B | A | B | 
|---|---|---|
| 0 | 0 | 0 | 
| 0 | 1 | 1 | 
| 1 | 0 | 1 | 
| 1 | 1 | 1 | 
| はビット論理和(OR)演算子である. 二つの整数値 a, b に対して, a | b は対応するビットごとに論理和 (OR) をとった結果を返す.
1ビットの値 A, B に対する A | B の値は右の表の通り.
| 演算子は,いくつかに限定したビットの状態を 1 にセットしたものを得たいときに,よく使われる. たとえば,char 型 8 ビットのうち,下位の 4 ビットをすべて 1 にセットしたければ, 00001111(2) = 15 = 0f(16) とのビット論理和をとればよい. 次のプログラム中の z = x | y という文では, x = 0x6a = 01101010(2) の下位 4 ビットを 1 にセットしたものを z に代入している.
int main()
{
  unsigned char x, y, z;
  x = 0x6a;
  y = 0x0f;
  z = x | y;
  show_unsigned_char(x);
  show_unsigned_char(y);
  show_unsigned_char(z);
  
  return 0;
}
106 6a 01101010 15 0f 00001111 111 6f 01101111
また, x = x | y の代わりに x |= y と書くこともできる.
x |= y; /* x = x|y と同じ */
| A | B | A ^ B | 
|---|---|---|
| 0 | 0 | 0 | 
| 0 | 1 | 1 | 
| 1 | 0 | 1 | 
| 1 | 1 | 0 | 
^ はビット排他的論理和(XOR)演算子である. 二つの整数値 a, b に対して, a ^ b は対応するビットごとに排他的論理和 (XOR) をとった結果を返す ( ^ はキャレットあるいはハットと読む).
1ビットの値 A, B に対する A ^ B の値は右の表の通り.
^ 演算子は,いくつかに限定したビットの状態を反転させたものを得たいときに,よく使われる. たとえば,char 型 8 ビットのうち,下位の 4 ビットを反転したければ, 00001111(2) = 15 = 0f(16) とのビット排他的論理和をとればよい. 次の例では, x = 0x6a = 01101010(2) の下位4ビットを反転させたものを z に入れている.
int main()
{
  unsigned char x, y, z;
  x = 0x6a;
  y = 0x0f;
  z = x ^ y;
  show_unsigned_char(x);
  show_unsigned_char(y);
  show_unsigned_char(z);
  
  return 0;
}
106 6a 01101010 15 0f 00001111 101 65 01100101
また, x = x ^ y の代わりに x ^= y と書くこともできる.
x ^= y; /* x = x^y と同じ */
排他的論理和は非常に面白い演算であり,次のような性質がある.
(1)  x ^ 0 == x
(2)  x ^ x == 0
(3)  (x ^ y) ^ z == x ^ (y ^ z)
この性質を用いると,簡単な暗号システムが作れる.
数値 x を鍵 k で暗号化したければ,x と k とのビット排他的論理和をとり
   y = x ^ k;
とする.これを復号化したければ,もう一度 k とのビット排他的論理和をとる.
   z = y ^ k;
このとき,z の値は x の値に等しい.なぜならば,
   z == y ^ k == (x ^ k) ^ k == x ^ (k ^ k) == x ^ 0 == x
だからである.
| A | ~A | 
|---|---|
| 0 | 1 | 
| 1 | 0 | 
~ はビット反転演算子あるいはビット否定(NOT)演算子である. 整数値 a に対して, ~a は対応するビットごとにそれを反転した結果を返す. (~ はチルダーと読む)
1ビットの値 A に対する ~A の値は右の表の通り.
~ 演算子は,いくつかに限定したビットだけが 0 で, その他のすべてのビットが 1 であるような値を得たいときに,よく使われる. たとえば,下位の3ビットだけが 0 で残りのビットが 1 であるような整数値 111...111000(2) は 000...000111(2) = 7 に対して ~ を施すと得られる. (その利点は,全体が何ビットあるかを知らなくてもよいということである.)
int main()
{
  unsigned char x, y;
  x = 7;
  y = ~x;
  show_unsigned_char(x);
  show_unsigned_char(y);
  
  return 0;
}
7 07 00000111 248 f8 11111000
コンピュータの環境によっては,~ という文字がオーバースコア  ̄ となることもある.
整数値を表すビットは,2進数表記と同じように,横に一列に並んでいると見なされている. 右端が最下位ビットであり,左端が最上位ビットである.
このビットパターンを,横にずらすことをシフトという. 特に左にずらすことを左シフト,右にずらすことを右シフトという.
整数値 a と n とに対して, a << n は a を表すビットパターンを左に n ビットだけずらして得られる整数値を返す. ただし,左にずらしたときに右側に空くビットには 0 が入る.
次の例は, 11 = 00001011(2) を左シフトして得られる値を順に表示している.
int main()
{
  unsigned char x = 11;
  show_unsigned_char(x);
  show_unsigned_char(x << 1);
  show_unsigned_char(x << 2);
  show_unsigned_char(x << 3);
  show_unsigned_char(x << 4);
  show_unsigned_char(x << 5);
  show_unsigned_char(x << 6);
  show_unsigned_char(x << 7);
  show_unsigned_char(x << 8);
  
  return 0;
}
11 0b 00001011 22 16 00010110 44 2c 00101100 88 58 01011000 176 b0 10110000 96 60 01100000 192 c0 11000000 128 80 10000000 0 00 00000000
最初のうちは, 11, 22, 44, 88, 176 と数値が倍々に増えていることに注意しよう. しかし,最上位ビットが1になってそれがさらに左にシフトされて押し出されると, 値が下がってしまう.最後はすべてのビットが左に押し出されて, 0 になってしまう.
シフト演算子は,変数自身の値を変化させるわけではないので,変数 x の値をシフトして変化させたければ x = x << n; あるいは x <<= n; とする必要がある.
x <<= n; /* x = x << n と同じ */
整数値 a と n とに対して, a >> n は a を表すビットパターンを右に n ビットだけずらして得られる整数値を返す.
この演算子は,符号無しの数値に施したときと符号付きの数値に施したときとで, その結果が異なる.
左シフトのときと同様に,右にビットパターンをずらしたときに左側に空いたビットには 0 が入る.
このようなシフトを論理シフトと呼ぶこともある.
次の例は, 160 = 10100000(2) を右シフトして得られる値を順に表示している.
int main()
{
  unsigned char x;
  x = 160;
  show_unsigned_char(x);
  show_unsigned_char(x >> 1);
  show_unsigned_char(x >> 2);
  show_unsigned_char(x >> 3);
  show_unsigned_char(x >> 4);
  show_unsigned_char(x >> 5);
  show_unsigned_char(x >> 6);
  show_unsigned_char(x >> 7);
  show_unsigned_char(x >> 8);
  
  return 0;
}
160 a0 10100000 80 50 01010000 40 28 00101000 20 14 00010100 10 0a 00001010 5 05 00000101 2 02 00000010 1 01 00000001 0 00 00000000
160, 80, 40, ... と,1つ右にシフトするごとに数値が約半分, 正確には 1/2倍して端数は切り捨てになっていることに注意しよう. 最後はすべてのビットが右に押し出されて, 0 になってしまう.
シフト演算子は,変数自身の値を変化させるわけではないので,変数 x の値をシフトして変化させたければ x = x >> n; あるいは x >>= n; とする必要がある.
x >>= n; /* x = x >> n と同じ */
符号付き整数型の場合,右にビットパターンをずらしたときに左側に空いたビットには 符号ビット(一番左側のビット)と同じ値が入る.
このようなシフトを算術シフトと呼ぶこともある.
次の例は, 100 = 01100100(2) と -100 = 10011100(2) とに対して,それらを右シフトして得られる値を順に表示している.
int main()
{
  unsigned char x;
  x = 100;
  show_signed_char(x);
  show_signed_char(x >> 1);
  show_signed_char(x >> 2);
  show_signed_char(x >> 3);
  show_signed_char(x >> 4);
  show_signed_char(x >> 5);
  show_signed_char(x >> 6);
  show_signed_char(x >> 7);
  printf("\n");
  
  x = -100;
  show_signed_char(x);
  show_signed_char(x >> 1);
  show_signed_char(x >> 2);
  show_signed_char(x >> 3);
  show_signed_char(x >> 4);
  show_signed_char(x >> 5);
  show_signed_char(x >> 6);
  show_signed_char(x >> 7);
  
  return 0;
}
100 64 01100100 50 32 00110010 25 19 00011001 12 0c 00001100 6 06 00000110 3 03 00000011 1 01 00000001 0 00 00000000 -100 9c 10011100 -50 ce 11001110 -25 e7 11100111 -13 f3 11110011 -7 f9 11111001 -4 fc 11111100 -2 fe 11111110 -1 ff 11111111
x が正の値のときには,符号無しの型のときと同様に左側に空いたビットには 0 が入るが, x が負の値の時には,左側に空いたビットには 1 が入る. どちらにせよ,一つシフトするごとに値が 1/2倍(端数は切り下げ)になることに注意しよう.
シフトを続けると,x が正の数のときには,すべてが右に押し出されて 0 となるが, x が負の値のときには,全てが符号ビットで埋め尽くされて -1 となる.
右シフトで左側にあいたビットを符号ビットの値で埋める理由は, 始めの値と同じ符号を持たせるためである. それゆえ,算術シフトと呼ばれる.
シフト演算子は,変数自身の値を変化させるわけではないので,変数 x の値をシフトして変化させたければ x = x >> n; あるいは x >>= n; とする必要がある.
x >>= n; /* x = x >> n と同じ */
| 1 | ~ | 
| 2 | << >> | 
| 3 | & | 
| 4 | ^ | 
| 5 | | | 
異なる種類のビット演算子を一つの式の中に使った場合,その優先順位は右の表のようになる (1 が高くて 5 が低い).
例えば, a | b & c という式は a | (b & c) と同じであり, 次の2つの式も同じである.
f = a | b & ~ c << d ^ e; f = a | ((b & ((~ c) << d)) ^ e);
以上では,ビット演算子を説明するのに char 型で説明してきたが,ビット演算子は主に int 型に 対して働くように設計されている.実は, char 型は大きさが中途半端でビット演算子 (特にシフト演算子)がうまく働かない場合がある.そこで,これ以降は, int 型に対して施すことにする.
まず, unsigned int 型のビット状態を表示する関数を作って, ビット演算子をいくつか試して見てみよう.
#include <stdio.h>
void show_unsigned_int(unsigned int x)
{
  int i;
  printf("%10u  %08x  ", x, x);
  for (i = 31; i >= 0; i--) {
    printf("%d", (x>>i) & 1);
  }
  printf("\n");
}
int main()
{
  unsigned int x = 0x0000ffff;
  show_unsigned_int( x );
  show_unsigned_int( x << 8 );
  show_unsigned_int( x & (x << 8) );
  show_unsigned_int( x ^ (x << 8) );
  show_unsigned_int( ~ (x ^ (x << 8)) );
  return 0;
}
     65535  0000ffff  00000000000000001111111111111111
  16776960  00ffff00  00000000111111111111111100000000
     65280  0000ff00  00000000000000001111111100000000
  16711935  00ff00ff  00000000111111110000000011111111
4278255360  ff00ff00  11111111000000001111111100000000
ビット演算子を用いたクイズです.
先ほどのプログラムのように, 数値 0x0000ffff (=00000000000000001111111111111111(2))が入った変数 x と ビット演算子とを用いて, 次のような結果になるようにせよ. (シフト演算子のシフト量を指定する数値以外は,数値を用いてはならない. 式中の演算子の個数と変数 x の個数とができるだけ少ないようにせよ.)
     98304  00018000  00000000000000011000000000000000
 268435440  0ffffff0  00001111111111111111111111110000
1056964860  3f0000fc  00111111000000000000000011111100
 267390960  0ff00ff0  00001111111100000000111111110000
次のプログラムにある show_bit_figure という関数と,ビット演算子とを用いて, ビットで好きなようにアートを作れ. プログラムのサンプルを書いておいたが,それにとらわれず自由に作ってもよい.
#include <stdio.h>
void show_bit_figure(unsigned int x)
{
  int i;
  printf("%10u  %08x  ", x, x);
  for (i = 31; i >= 0; i--) {
    if((x>>i) & 1 == 1) {
      printf(" o");
    } else {
      printf(" .");
    }
  }
  printf("\n");
}
int main()
{
  int i;
  for (i = 0; i < 32; i++) {
    show_bit_figure( ((0x80000000 >> i) | (0x00000001 << i)) ^ 0xff0000ff );
  }
  printf("\n");
  for (i = 0; i < 32; i++) {
    show_bit_figure( (0x0000ffff << (i % 16)) ^ (0xffff0000 >> (i % 16)) );
  }
  return 0;
}
2130706686  7f0000fe   . o o o o o o o . . . . . . . . . . . . . . . . o o o o o o o .
3204448509  bf0000fd   o . o o o o o o . . . . . . . . . . . . . . . . o o o o o o . o
3741319419  df0000fb   o o . o o o o o . . . . . . . . . . . . . . . . o o o o o . o o
4009754871  ef0000f7   o o o . o o o o . . . . . . . . . . . . . . . . o o o o . o o o
4143972591  f70000ef   o o o o . o o o . . . . . . . . . . . . . . . . o o o . o o o o
4211081439  fb0000df   o o o o o . o o . . . . . . . . . . . . . . . . o o . o o o o o
4244635839  fd0000bf   o o o o o o . o . . . . . . . . . . . . . . . . o . o o o o o o
4261412991  fe00007f   o o o o o o o . . . . . . . . . . . . . . . . . . o o o o o o o
4286579199  ff8001ff   o o o o o o o o o . . . . . . . . . . . . . . o o o o o o o o o
4282385151  ff4002ff   o o o o o o o o . o . . . . . . . . . . . . o . o o o o o o o o
4280288511  ff2004ff   o o o o o o o o . . o . . . . . . . . . . o . . o o o o o o o o
4279240959  ff1008ff   o o o o o o o o . . . o . . . . . . . . o . . . o o o o o o o o
4278718719  ff0810ff   o o o o o o o o . . . . o . . . . . . o . . . . o o o o o o o o
4278460671  ff0420ff   o o o o o o o o . . . . . o . . . . o . . . . . o o o o o o o o
4278337791  ff0240ff   o o o o o o o o . . . . . . o . . o . . . . . . o o o o o o o o
4278288639  ff0180ff   o o o o o o o o . . . . . . . o o . . . . . . . o o o o o o o o
4278288639  ff0180ff   o o o o o o o o . . . . . . . o o . . . . . . . o o o o o o o o
4278337791  ff0240ff   o o o o o o o o . . . . . . o . . o . . . . . . o o o o o o o o
4278460671  ff0420ff   o o o o o o o o . . . . . o . . . . o . . . . . o o o o o o o o
4278718719  ff0810ff   o o o o o o o o . . . . o . . . . . . o . . . . o o o o o o o o
4279240959  ff1008ff   o o o o o o o o . . . o . . . . . . . . o . . . o o o o o o o o
4280288511  ff2004ff   o o o o o o o o . . o . . . . . . . . . . o . . o o o o o o o o
4282385151  ff4002ff   o o o o o o o o . o . . . . . . . . . . . . o . o o o o o o o o
4286579199  ff8001ff   o o o o o o o o o . . . . . . . . . . . . . . o o o o o o o o o
4261412991  fe00007f   o o o o o o o . . . . . . . . . . . . . . . . . . o o o o o o o
4244635839  fd0000bf   o o o o o o . o . . . . . . . . . . . . . . . . o . o o o o o o
4211081439  fb0000df   o o o o o . o o . . . . . . . . . . . . . . . . o o . o o o o o
4143972591  f70000ef   o o o o . o o o . . . . . . . . . . . . . . . . o o o . o o o o
4009754871  ef0000f7   o o o . o o o o . . . . . . . . . . . . . . . . o o o o . o o o
3741319419  df0000fb   o o . o o o o o . . . . . . . . . . . . . . . . o o o o o . o o
3204448509  bf0000fd   o . o o o o o o . . . . . . . . . . . . . . . . o o o o o o . o
2130706686  7f0000fe   . o o o o o o o . . . . . . . . . . . . . . . . o o o o o o o .
4294967295  ffffffff   o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o
2147385342  7ffe7ffe   . o o o o o o o o o o o o o o . . o o o o o o o o o o o o o o .
1073496060  3ffc3ffc   . . o o o o o o o o o o o o . . . . o o o o o o o o o o o o . .
 536354808  1ff81ff8   . . . o o o o o o o o o o . . . . . . o o o o o o o o o o . . .
 267390960  0ff00ff0   . . . . o o o o o o o o . . . . . . . . o o o o o o o o . . . .
 132122592  07e007e0   . . . . . o o o o o o . . . . . . . . . . o o o o o o . . . . .
  62915520  03c003c0   . . . . . . o o o o . . . . . . . . . . . . o o o o . . . . . .
  25166208  01800180   . . . . . . . o o . . . . . . . . . . . . . . o o . . . . . . .
         0  00000000   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  25166208  01800180   . . . . . . . o o . . . . . . . . . . . . . . o o . . . . . . .
  62915520  03c003c0   . . . . . . o o o o . . . . . . . . . . . . o o o o . . . . . .
 132122592  07e007e0   . . . . . o o o o o o . . . . . . . . . . o o o o o o . . . . .
 267390960  0ff00ff0   . . . . o o o o o o o o . . . . . . . . o o o o o o o o . . . .
 536354808  1ff81ff8   . . . o o o o o o o o o o . . . . . . o o o o o o o o o o . . .
1073496060  3ffc3ffc   . . o o o o o o o o o o o o . . . . o o o o o o o o o o o o . .
2147385342  7ffe7ffe   . o o o o o o o o o o o o o o . . o o o o o o o o o o o o o o .
4294967295  ffffffff   o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o
2147385342  7ffe7ffe   . o o o o o o o o o o o o o o . . o o o o o o o o o o o o o o .
1073496060  3ffc3ffc   . . o o o o o o o o o o o o . . . . o o o o o o o o o o o o . .
 536354808  1ff81ff8   . . . o o o o o o o o o o . . . . . . o o o o o o o o o o . . .
 267390960  0ff00ff0   . . . . o o o o o o o o . . . . . . . . o o o o o o o o . . . .
 132122592  07e007e0   . . . . . o o o o o o . . . . . . . . . . o o o o o o . . . . .
  62915520  03c003c0   . . . . . . o o o o . . . . . . . . . . . . o o o o . . . . . .
  25166208  01800180   . . . . . . . o o . . . . . . . . . . . . . . o o . . . . . . .
         0  00000000   . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
  25166208  01800180   . . . . . . . o o . . . . . . . . . . . . . . o o . . . . . . .
  62915520  03c003c0   . . . . . . o o o o . . . . . . . . . . . . o o o o . . . . . .
 132122592  07e007e0   . . . . . o o o o o o . . . . . . . . . . o o o o o o . . . . .
 267390960  0ff00ff0   . . . . o o o o o o o o . . . . . . . . o o o o o o o o . . . .
 536354808  1ff81ff8   . . . o o o o o o o o o o . . . . . . o o o o o o o o o o . . .
1073496060  3ffc3ffc   . . o o o o o o o o o o o o . . . . o o o o o o o o o o o o . .
2147385342  7ffe7ffe   . o o o o o o o o o o o o o o . . o o o o o o o o o o o o o o .