2 コンピューター

2.1 コンピューターの基本構成

コンピューターは複雑な装置であるが、図1のような機能の 集まりに分解できる2。それぞれの機能(装置)は、制御信号線とデータ信号線で 接続されている。そこを、0と1のパルスの信号が信じられないくらい高速でかつ調和を取って流れ、全体としてコンピューターが動作するのである。大量の信号が一つも間違いなく伝送されるのは驚きであ る。この信号線は人間で言えば神経に当たり、そこに流れる内容(情報)は、
制御信号
「・・・しなさい」とハードウェアーに動作の指示をする信号
データ信号
「・・・を」とデータや命令の内容を示す信号
である。

コンピューターの基本構成は図1に示したとおりであるが、 制御装置と演算装置、記憶装置、入力装置、 出力装置を五大装置と呼ぶ。それぞれは、次のような働きがある。

制御装置
主記憶装置(メインメモリー)に格納されているプログラム(命 令)を受け取り、それを解読し、電気信号に変え、各装置に指 令を出す。
演算装置
主記憶装置に格納されているデータを受け取り、制御装置の 指令に従い、それを加工(処理)する。
記憶装置
記憶装置は、主記憶装置と補助記憶装置がある。
主記憶装置
命令とデータからなるプログラムを格納する。
補助記憶装置
主記憶装置には容量に制限があるので、それを越えるものをここ に格納する。また、主記憶装置は電源を切ると、データが失われる ので、半永久的に残したい場合、補助記憶装置に格納する。ハード ディスクやCD-ROMがこれに当たる。
入力装置
コンピューターの外部からデータを取り込む装置である。キー ボードやマウス等がこれに当たる。
出力装置
主記憶装置に格納されているデータをコンピューター外部に 出力する装置である。ディスプレイやプリンター等がこれに当 たる。
モデムやLANカードのように、入出力装置にもなっているものがあるので注意が必要であ る。

これらのなかで、制御装置と演算装置をまとめて中央制御装置(CPU:Central Processing Unit)と言う。これを一つのチップにまとめたものを MPU(Micro Processing Unit) と言 うことになっている。ただ、MPUとCPUはほとんど同義語として使われるので、本講義では 全て CPU に統一する。その方が諸君もなじみ深いであろう。

図 1: コンピューターの基本構成
\includegraphics[keepaspectratio, scale=1.0]{figure/comp_5_equipments.eps}

2.2 メモリーとCPU

ここで、コンピューターを構成する最小の部品を考える。そうするとCPUとメインメモリー があれば良いことが分かる。これでも、メインメモリーにプログラムを格納して、CPUと データの受け渡しを行い、データを処理することができる。図2のよ うなものである。事実、CPUと入出力装置の間に流れるデータは、メインメモリーを介し ている。従って、コンピューターの原理的なモデルを図2のように考 えても良いだろう。

プログラマーはメモリーの内容について、ある程度自由に変更ができる。そのようなこと から、メモリーを意識してプログラムを作成することが重要である。アセンブラー言語を使うと なるとCPUについても意識が必要であろうが、C言語ではそこまで要求しない。

本日のメインテーマはポインターであるが、それを深く理解するために、メモリーの知識 が必要である。しかし、そんなに難しいことではない。

図 2: もっとも原始的なコンピューター
\includegraphics[keepaspectratio, scale=1.0]{figure/CPU_memory.eps}


2.3 メインメモリーのモデル

メインメモリーのハードウェアーの構造については、ここではどうでも良い。それよりか、 メインメモリーのモデルを理解することが重要である。

メインメモリーの役目は、命令とデータからなるプログラムを記憶することである。そのプロ グラムは全て、0と1の数字で表せ、2進数で表現可能である。どのようなモデルでこの2進 数が格納されているか学習する。

プログラムという情報は記憶するだけでは全く役に立たない。記憶した内容を取り出せて 初めて、活用ができる。そこで、メモリーは記憶するための住所が決められている。この 住所のことをアドレス(address)と言い、0から整数の番地がふってある。諸君が使ってい るパソコンのアドレスは32ビットで表現されている3。そして、一つの番地には、8個の0と1が記憶 できる。この様子を図3に示す。

図を見て分かるとおり、2進数の表現は桁数が多くて人間にとって大変である4。そこで、通常は、2進数の4桁をまとめて、16進数で 表す。そうすると、アドレスは16進数8桁、記憶内容は16進数2桁で表すことができ、分か りやすくなる。その様子を図4に示す。

ついでに述べておくが、1個の0あるいは1の情報量を1ビットと言う。8ビットで1バイトと 言う。従って、メインメモリーの一つの番地(アドレス)には、1バイト(8ビット)の情報が 記憶できる。

メモリーについて覚えておくことは、以下の通りである。

図 3: メモリーのモデル(2進数)
\includegraphics[keepaspectratio, scale=1.0]{figure/memory_binary.eps}
図 4: メモリーのモデル(16進数)
\includegraphics[keepaspectratio, scale=1.0]{figure/memory_hexadecimal.eps}

2.4 データの型とバイト数

諸君が使う変数の型は、文字型と整数型、倍精度実数型がほとんどである。秋田高専内で 使用されているパソコンのそれぞれのサイズは表1の通りであ る。文字型であれば、一つのアドレス内に格納することができるが、整数型では4つ、 倍精度実数型では8個のアドレスが必要である。一つのアドレスに一つのデータが記憶さ えれている訳ではない。付録のリスト3にデータ型のバイト数を調べ るプログラムを載せておく。

表 1: 変数の型とバイト数
型名 データ型 バイト数 ビット数
文字型 char 1 8
整数型 int 4 32
倍精度実数 double 8 64

それでは、実際にメモリーにデータが格納する様子を見よう。次のようにプログラムに書 いたとする。

  double x=-7.696151733398438e-4;
  int i=55;
  char a='a';
それぞれのデータは、 となっている。実際にこれを確かめるプログラムを、付録のリスト4 に示している。このプログラムを私のパソコンで実行させると、図 5のようなメモリー配置になっていることが分かっ た。表1の通り、整数型と実数型は複数のアドレスにわたって データが格納されていることが分かる。

鋭い学生は、データの並びが逆であることが分かるであろう。例えば、整数のiであ るが、 $ (55)_{10}=(00000037)_{16}$なので、 $ 00\rightarrow 00\rightarrow
00\rightarrow 37$と並ぶと考えられるが、実際は図 5の通り逆である。これはCPUがそのように作られ ているからである。このように逆に配置させる方法をリトルエンディアンと言 う。Intel社のCPUはリトルエンディアンである。一方、そのままのメモリーに配置する方 法はビッグエンディアンと呼ばれる。このようにメモリーにデータを並べる方 法は2通りあって、それをバイトオーダーと言う。

ここで、理解しておくべきことは、以下の通りである。

図 5: メモリー中に格納されたデータの例
\includegraphics[keepaspectratio, scale=1.0]{figure/data_in_memory_hexadecimal.eps}

2.5 プログラムが格納される様子

これまでは、メモリーのデータの格納方法を学習した。以前、プログラム (命令とデータ)は全てメモリーに格納されると述べた。ここでは、もう少し進んで、プロ グラムがメモリーの中にどのように格納されているか調べてみよう。この辺のことと、マ シン語が分かると、ハッカー(クラッカーと言った方が適切かも)になれるかも・・・。

それでは、リスト1に示す簡単なプログラムで、データとメモリー の格納アドレスを調べてみよう。このプログラムの内容は、以下の通りである。まだ、詳 細は分からなくても良いが、大体の流れをつかんで欲しい。

1行
今のところおまじない
2行
関数funcのプロトタイプ宣言
4-6行
コメント文。プログラムの動作には無関係。プログラマーのために記述。
7行
main 関数の始まり。int で整数を返すことを示し、void で引数が 無いことを示している。
9行
整数変数 i の宣言
11行
結果を分かりやすくするために -- address ------ を表示。最後 に \n で改行。
12行
main 関数が書かれている先頭アドレスを表示。関数名はアドレスを表 しており、変換指定子 %p でディスプレイに表示。 \t は、タブを表し、適当な空白が入る。
13行
関数 func が書かれている先頭アドレスを表示。
14行
変数 i の先頭アドレスを表示。変数名に & を付けると、その先頭 アドレスを示すことになる。& はアドレス演算子である。
16行
関数 func に処理が移り、その戻り値を変数 i に代入。
18行
main関数の終了を表し、呼び出し元(OS)に整数の 0 を返している。
19行
main関数のブロックの終わり。
21-23行
コメント文
24行
関数 func の始まり。int で整数を返すことを示している。仮引 数は、整数型の ij である。
26行
変数 i の先頭アドレスを表示。
27行
変数 j の先頭アドレスを表示。
29行
関数funcの終了を表し、呼び出し元(ここではmain関数)に整数の ij の積を返している。
31行
関数funcのブロックの終わり。

   1 #include <stdio.h>
   2 int func(int i, int j);
   3 
   4 /*======================================================*/
   5 /*   メイン関数                                          */
   6 /*======================================================*/
   7 int main(void){
   8 
   9   int i;
  10 
  11   printf("--- address -----------\n");
  12   printf("\tmain\t%p\n", main);
  13   printf("\tfunc\t%p\n", func);
  14   printf("\tmain-i\t%p\n",&i);
  15 
  16   i=func(5,3);
  17 
  18   return 0;
  19 }
  20 
  21 /*======================================================*/
  22 /*  func 関数                                           */
  23 /*======================================================*/
  24 int func(int i, int j){
  25 
  26   printf("\tfunc-i\t%p\n",&i);
  27   printf("\tfunc-j\t%p\n",&j);
  28 
  29   return i*j;
  30 
  31 }
\fbox{実行結果}
	--- address -----------
	        main    0x8048368
	        func    0x80483eb
	        main-i  0xbffff6b4
	        func-i  0xbffff690
	        func-j  0xbffff694
実行結果から、命令とデータは図6のようになっ ていることが分かるであろう。命令である関数は、大体近くのメモリ上に配置されている。 しかし、データの内容を格納する変数は、ずっと離れてところにメモリーが割り当てられ ている。

関数の中で宣言される変数は、ローカル変数と言い、その宣言した関数でのみアクセスが 可能である。従って、同じ名前であるが、違う関数で宣言されたローカル変数は全く別物であ る。図6で分かるように、関数mainと関数 funcで同じ名前のローカル変数 i を宣言してるがメモリー上の配置は全く異 なる。このことからも、名前は同じであるが、全く違うものであることが理解できる。

ここで、理解しておくべきことは、以下の通りである。

図 6: プログラムのメモリーへの格納。記憶の内容は不明なので、??としてい る。
\includegraphics[keepaspectratio, scale=1.0]{figure/program_in_memory_hexadecimal.eps}

ホームページ: Yamamoto's laboratory
著者: 山本昌志
Yamamoto Masashi
平成19年6月24日


no counter