第十七話 メモリーの世界
立花先輩の講義は続く。
「じゃイツキくん。さっき覚えた2進数をさっそく使う事になるわ」
「はい」
「まず、コンピューターでは2進数のそれぞれの桁のことをビットって数えるの」
「ビット?」
「そう。1ビットは2進数の1桁のことね。0か1のどちらかのデータが入っているの。たとえば2ビットだったら、2桁の2進数、00、01、10、11のどれかね。4ビットだったら、4桁の0000とか1111とか」
「それがメモリーと関係あるんですか?」
「大ありよ。コンピューター上は、メモリーがビットで並んでいるのだから。でも、これだと桁が大きくなりすぎるから、8ビットずつでまとめて、バイトって数の単位で数えることになってるの」
「バイト……あぁ、聞いたことあるなぁ」
前に先輩も言ってたな、そういりゃ。
「1バイト = 8ビットって覚えていいわ。実は、そうでないコンピューター、1バイトが9ビットの処理系なんてのも世の中にはあるんだけど、イツキくんが出会う事はまずないから」
「そ、そうなんですか?」
「うん。で、話をビットに戻すと、8ビットだと、どれだけの数が表せるかわかる? えーっと、西原さん」
「はい」西原さんが考え始める。「そうですねー、1ビットだと1と0で2種類、2ビットだと、00、01、10、11の4種類だから……8ビットなら、2の8乗で256でしょうか」
「うん。さすが。2、4、8、16、32、64、128、256ね。で、それを16進数にしたら、00からFFまでの数になる。ほら、キッチリと並べられるでしょ? 10進数にしたら、こう綺麗に並べられないわけ」
「うーん、考えられているなぁ」
「そりゃもう、イツキくんやあたしらが産まれる前から、数学者やコンピューター科学者らが何十年も考え続けた世界だもの。当たり前でしょー」
うはっ、そりゃそうだ。
「でね、メモリーの話に戻すと、コンピューターには、このバイト単位で情報が並べられているわけ。この情報には、プログラムで使う変数とかもそうだし、プログラムそのものすら、そう」
「プログラムも、メモリーに?」
「そう。実行開始時に、OSがハードディスクからメモリーにすべてコピーするのよ。で、Cで使う変数も、メモリー上の特定の場所にデータとしてあるわけね。この特定の場所のことをメモリーアドレスって言うんだけど。アドレスxx番地に、変数Aのデータが入っているって感じ」
「マンションの入り口の郵便箱が並んでる処で、特定の箱の場所に手紙が入っているってイメージですね」と西原さん。
うーむ。俺はだんだんとコンピュータの中の世界がわかってきた気がする……。
「じゃ、次には変数の型について、より詳しく突っ込んでみるわよ。実は、メモリーに並ぶ変数は、1バイトずつってわけじゃないのよ。2バイト取る型の変数もあれば、4バイト取る型の変数もある」
「そうなんですか?」
「たとえばcharの場合、1バイト。intの場合は、4バイト使うって感じ。ちなみに、char=1バイトを除いて、型のバイト数が規格で決まっている訳でもないのがややこしいんだけど、大半のはそうだと思っていいわよ」
「変数のバイト数が違うと何が違うのだろう?」
「イツキくん、そりゃ使える数の量でしょ。たとえばcharだったら、0から255までの256種類ってなってるでしょ。でも、もし2バイトの型の変数だったら――」
西原さんが計算する。
「2バイト使う変数、えーっとshortかな――だと、2の16乗だと、0から65535までですね」
「西原さん、マイナスを忘れないでよ。一番上位のビットはプラス、マイナスの符号に使うから、半分の-32768 ~ 32767までになるのよ。もっとも、unsigned shortにしたら話は別だけど」
「あ、そうでしたねー」
西原さんが頷く。
「なるほど、それで、いろいろと型の種類があるんだ。バイトが違うんだな」
「ま、そうね。じゃ、ここらへんで理論は置いて、実践と行くわよ」
先輩がWindowsのメモ帳を開くと、カタカタとプログラムを打ち込み始めた。
「今回は、変数のメモリーアドレスを見るプログラムよ。それには、printf関数のフォーマット引数に、『%p』を指定すると出せるの。それから、変数の方も、&を前につけないといけないわ。じゃないと、単に変数の中の値が表示されちゃう」
「あっ、&の変数って、そういう事だったのか!」
俺は思い出した。前にもそういう引数を取る関数があったな。
「うんうん。今のイツキくんなら、意味がわかるんじゃないかなー」
先輩はふふっと笑った。
「よしっ。出来た―」
俺と西原さんがコードを見る。
「配列変数を3つずつ作ったわ。配列はメモリー上で並んでいるのが保証されているから、この実験に使うのには、ぴったりなの」
/* メモリーの検証 memtest.c */
#include <stdio.h>
int main(void)
{
char c[3];
int i[3];
double d[3];
printf("変数c[0] のメモリーアドレス:%p \n",&c[0]);
printf("変数c[1] のメモリーアドレス:%p \n",&c[1]);
printf("変数c[2] のメモリーアドレス:%p \n",&c[2]);
printf("¥n“);
printf("変数i[0] のメモリーアドレス:%p \n",&i[0]);
printf("変数i[1] のメモリーアドレス:%p \n",&i[1]);
printf("変数i[2] のメモリーアドレス:%p \n",&i[2]);
printf("\n");
printf("変数d[0] のメモリーアドレス:%p \n",&d[0]);
printf("変数d[1] のメモリーアドレス:%p \n",&d[1]);
printf("変数d[2] のメモリーアドレス:%p \n",&d[2]);
return 0;
}
「ま、プログラムの解説は必要ないよね。さっそく実行するよー」
先輩はコマンドプロンプトでコードをコンパイルすると、実行した。
C:\sample>memtest
変数c[0]のメモリーアドレス:0022FF1D
変数c[1]のメモリーアドレス:0022FF1E
変数c[2]のメモリーアドレス:0022FF1F
変数i[0]のメモリーアドレス:0022FF10
変数i[1]のメモリーアドレス:0022FF14
変数i[2]のメモリーアドレス:0022FF18
変数d[0]のメモリーアドレス:0022FEF8
変数d[1]のメモリーアドレス:0022FF00
変数d[2]のメモリーアドレス:0022FF08
C:¥sample>_
「この右側のが、メモリーアドレスか……」俺は呟く。
「ちなみに、毎回実行するたびにアドレスは変化するから、意味を考えても無駄よ。OSが適当に空いているトコを使うだけだから」
「は、はあ」
「で、注目すべきなのが、配列変数のお互いの間隔。charだと、D、E、Fと1バイトずつになっているでしょ」
「あ、確かに」
「で、intの場合は、このパソコンだと4バイトね。10、14、18。そして細かい小数点を使うdoubleだと8バイト必要になっちゃう。FEF8、FF00、FF08って」
「FEF8、F9、FA、FB、FC、FD、FE、FF……確かに8バイト分ありますねぇ。その次がFF00のインデックス1の配列変数の場所になりますし」と西原さんも頷く。
「これで、イツキくんも、コンピューターのメモリについてわかったと思う」
「ええ。結構わかった気がしますよ」
「で、そのアドレスの知識が実は、これから話す内容にとって、最重要になるのよ」先輩は力こぶしを作って力説した。
「なっ、なにを……」
先輩のただならぬ様子に、俺は少しビビっていた。
「ふっふっふっ。覚悟することね、イツキくん。Cの最大の難関、かつて多数の挑戦者を屍とし、多数をJavaへ逃げ出させた話になるわよ……」
先輩は薄く笑った。
いったい、先輩は何を話そうとしているのか!?