第2章16 型の長さ、情報量とビットを知る@イチからゲーム作りで覚えるC言語

イチからゲーム作りで覚えるC言語
連載ページ一覧へ
2章17 暗黙の型変換ルールを知る
NEXT : 2章17 暗黙の型変換ルールを知る
2章15 switch文 条件で実行する処理を分ける : PREV

この記事でやること

前回は switch 文のキホンの書き方をお話ししました。
今回は、C言語で数値を扱うときの仕組みとビットについてお話します。C言語に限らずプログラミング全般的に通じるようなお話もあるので、ある程度仕組みを知っていることで、プログラムでのより正確な計算ができるようになると思います。今回はゲーム要素はなく、基礎知識的な部分ですので、わからなくなったら後から見返すようなつもりみてもらえればと思います。

この記事はゆる~くC言語でコンソールゲームを作りながらプログラミングを学ぶための連載記事です。シリーズものですので記事の一覧はこちらを参照してくださいね。

変数の型が使うサイズと、持てる数値の幅

前のお話のとき、いくつか代表的な変数についてお話しました。
整数を扱う変数の型によって、メモリ上で使用する量が異なり、メモリ上で使用する量が多いほど、変数が持てる数値の幅(種類)は大きくなることになります。
具体的に、どのように違うのかは、環境によって変わってきますので、実際にプログラムを動かして確認してみましょう。

#include <stdio.h>
#include <limits.h>
int main() {
 printf("1バイトは %d ビット\n", CHAR_BIT);
 printf("char  型は %d バイトで %d ~ %d の数をもてる\n", sizeof(char)  , CHAR_MIN, CHAR_MAX );
 printf("short 型は %d バイトで %d ~ %d の数をもてる\n", sizeof(short) , SHRT_MIN, SHRT_MAX );
 printf("int   型は %d バイトで %d ~ %d の数をもてる\n", sizeof(int)   , INT_MIN , INT_MAX);
 printf("long  型は %d バイトで %d ~ %d の数をもてる\n", sizeof(long)  , INT_MIN , INT_MAX);
 printf("unsigned char 型は %d バイトで %d ~ %d の数をもてる\n", sizeof(unsigned char), 0, UCHAR_MAX);
}

動かしてみると、筆者の環境では下のような結果になりました。
WindowsOS 上の VisualStudio Community 2017 で実行している人は同じ結果になると思います。

1バイトは 8 ビット
char 型は  1 バイトで -128 ~ 127 の数をもてる
short 型は 2 バイトで -32768 ~ 32767 の数をもてる
int 型は   4 バイトで -2147483648 ~ 2147483647 の数をもてる
long 型は  4 バイトで -2147483648 ~ 2147483647 の数をもてる
unsigned char 型は 1 バイトで 0 ~ 255 の数をもてる

ではでは、中身を見ていきましょう。

サンプルコードの中身

実際にソースコードを見てみましょう。
さっそくですが2行目に新しく「#include <limits.h>」という行があります。
「#include <ほにゃららぁ>」は、あらかじめ役に立つ便利な関数を、今回のソースコードの中で使えるようにする文です。
詳細は別のページでお話しますが、#include <limits.h> と記載があり、ある型(intとかcharとか)の最小の値、最大の値などの定義を確認することができるようになります。

#include <limits.h>

続いて5行目です。

 printf("1バイトは %d ビット\n", CHAR_BIT);

ここで、CHAR_BIT はビルドされるときには整数に置き換えられます。Visual Studio Community 2017で標準的な環境であれば、整数の 8 に置き換えられます。

つまり単純に「1バイトは 8 ビット」を文字として表示していることになります。
通常のコンピュータでは、ビットは 0 か 1 かの 2 通りを表す最小の単位となります。

1バイトは8ビットとはどういう意味?

パソコン内の適当なファイルを右クリックしてプロパティを確認すると、下のようが画面が出るかと思います。

ファイルのサイズ確認

ファイルのサイズ確認

この例だと 13,824 バイトと表示されているかと思います。これはこのファイルの情報量を表しています。
メモリやUSB、ハードディスク上には実際には 0 と 1 の羅列で保存されています。この 0 か 1 を持つことができるサイズが 1 ビット(bit)です。
1ビットは磁石でいえば S極とN極、電気信号でいえば電圧が加わっている、加わっていないなどの状態と置き換えることで、保存することができます。

最近の標準的に利用されるプログラムの実行環境では 1 バイト を 8 ビットとして処理しています。今後、このC言語のお話も 1 バイトは 8ビットとして記載していきます。

「ぷらんくの簡単ごはんレシピ.docx」は 13,824 × 8 ビットで作られており、つまりそれだけ 0 と 1 の組み合わせを使用しているサイズということになります。

C11規格でも1バイトは「8ビット以上」と定義していつつも、Visual Studio Community 2017 や GCC などの limits.h というファイル内にて、CHAR_BIT(1バイト)を 8 と定義しています。
昔は 1 バイトが 何ビットかの定義は処理系によって必要に応じてそれぞれ定義されていた(もしくは可変の概念として扱っていた)ようですが、最近のコンピュータでは 1 バイトは 8 ビットと事実上、決まっているようです。
元々 1 バイトを 7ビットとして扱っていたりするのは、一文字(英数字、特殊制御文字)を表現できるビット数を一つのカタマリとして考えていたためのようです。こちらに記載しているASCIIコード表だけであれば 7 ビットで表現することができますね。現在のコンピュータでは通常 1 バイトは 8 ビットの扱いです。
バイトなど、情報のサイズを表す単位については、下のTipsページにまとめましたので、よろしければどーぞ。
https://yukuto.net/blog/201805tips_quantities_and_units/

ビットの組み合わせで数値を表現

1ビットが持てる数の組み合わせは 2 種類のみとお話しました。このビットで数値を表すにはどのようにすればよいでしょうか。

たとえば2ビットで数値をどれくらい表現できるかを例に考えてみましょう。2ビットを使っての 0 と 1 の組み合わせはこんな数があります。

組み合わせ
パターン
2ビット目 1ビット目
A 0 0
B 0 1
C 1 0
D 1 1

パターンは 4 つありました。つまり、2ビットでは 4 種類の数を表現できることになります。
一般化して考えると、各ビットが 2 パターンあるので、 2 の 2 乗 = 4 パターンぶんの組み合わせ(数の種類)を表現できることになります。

3 ビットなら 2 の 3 乗ぶんで 8 パターン、
4 ビットなら 2 の 4 乗ぶんで 16 パターンという具合になり、
8 ビットなら 2 の 8 乗ぶんで 256 パターンを持つことができる、ということになります。

sizeof演算子とchar型が持てる数の範囲

ソースコードの6行目で、sizeof というキーワードが出てきました。
これは sizeof 演算子と呼ばれる、変わった演算子です。
使い方は

sizeof ( 型 )

という書き方をします。「型」は「int」だったり「char」だったりします。
sizeof 演算子は、指定した型がどのくらいのサイズをメモリ上で使うのか、バイト数で結果を返してくれます

また CHAR_MINCHAR_MAX は 「#include <limits.h>」を2行目に書いたことで使えるようになるキーワードであり、ビルドする時の処理(プリコンパイラ処理。詳細は後のページでお話します)でパソコンの環境に合わせて適切な整数に置き換わります。
具体的に、CHAR_MIN は char型の変数が持てる整数の最小、CHAR_MAX は char 型が持てる整数の最大に置き換わります。

 printf("char 型は %d バイトで %d ~ %d の数をもてる\n", sizeof(char) , CHAR_MIN, CHAR_MAX );

この行にあたる結果は下の通りでした。

char 型は  1 バイトで -128 ~ 127 の数をもてる

sizeof演算子でchar型を調べた結果を表示してみたところ、char 型は 1 バイトとなっていることがわかりました。1バイトは英数字などを1文字表示するためのサイズだったという事情もあり、char型が 1 バイトであることはC言語の仕様(C11 – 3.6)で決まっています。

また、1バイトは 8ビット計算であるとき、2の8乗(個)ぶんのビットの組み合わせがあり、256通りの数値を表現することができます。
これは、char型が -128 ~ 127 の幅の整数を持つことと一致します。(256通りの数値)

short 型、int型、long型の持てる数値の幅

ソースコードの7行目以降はこんな結果になっていました。

 short 型は 2 バイトで -32768 ~ 32767 の数をもてる
 int 型は 4 バイトで -2147483648 ~ 2147483647 の数をもてる
 long 型は 4 バイトで -2147483648 ~ 2147483647 の数をもてる

short 型は 2 バイトですので、メモリで16ビットを使用しています。
char 型は 1 バイトで固定ですが、short 型や int 型がいくつのバイト数を使うかは環境によって変わってきます。ルールとしては、ここに挙がっている型のサイズの順番として

char < short ≦ int ≦ long

であり、short は 2 バイト以上、intは 2 バイト以上、long は 4 バイト以上と記載があります。(参考:C11 – N1570 – 5.2.4.2.1 Sizes of integer types)

unsigned の指定

ソースコードの8行目を見てみましょう。

char 型の前に「unsigned」というキーワードが入っていますね。このキーワードを付けることで、マイナスの値を扱わず、その分の数字の範囲をプラスの数字の範囲に割り当てる、という意味となります。

 printf("unsigned char 型は %d バイトで %d ~ %d の数をもてる\n", sizeof(unsigned char), 0, UCHAR_MAX);

普通の char 型でしたら、1 バイトで -128 ~ 127 の間の 256通りの数字をもつことができました。

unsigned char 型であれば、 1 バイトで 0 ~ 255 の間の 256通りの数字をもつことができるようになります。
unsinged はプラスマイナスなどの「符号なしの」という意味となります

結果はこんな感じになっていると思います。

 unsigned char 型は 1 バイトで 0 ~ 255 の数をもてる

unsigned は char 型だけでなく、他の型(int型やfloat型)にも指定することができます。
メモリが少ない環境で、大量の 0 ~ 200 の数字を扱うときなど、short 型で1つの変数あたり 2 バイトを使わなくとも、unsigned char 型とすることで 1 バイトだけで 0 ~ 200 の数字を表現することができるなど、場合によって利点があります。

あとがき

今回はここまで。お疲れさまでした。

イチからゲーム作りで覚えるC言語
連載ページ一覧へ
2章17 暗黙の型変換ルールを知る
NEXT : 2章17 暗黙の型変換ルールを知る
2章15 switch文 条件で実行する処理を分ける : PREV

非常に参考になったサイトさまや、参考文献など

ページの更新履歴

更新日 更新内容
更新なし