コンピュータの中で、データが格納できる場所を、抽象的に 変数 ( variable ) と呼ぶことがあります。 格納したデータを、変数の値と呼びます。 変数にはデータが一つ格納でき、その値は読み取ったり変更したりできます。
変数にはデータが一つしか格納できません。 たくさんのデータを格納するには、他の抽象データ構造を利用します。 そのようなものの一つがスタックです。
スタック ( stack ) は、複数のデータが格納できる抽象データ構造です。 スタックには次々とデータが格納できますが、取り出せるのは最後に格納したデータのみです。
紙袋の中に雑誌を積み上げる様子を考えてください。 例えば、4月号の上に5月号をのせ、その上に6月号をのせます。 直接取り出せるのは6月号のみです。 6月号を取り除けば、5月号が取り出せます。 5月号も取り除き、7月号をのせ、その上に8月号をのせます。 すると、8月号、7月号、4月号の順に取り出せます。 以上がスタック操作の例です。
スタックにデータを格納することを、 プッシュ ( push ) と言います。 スタックからデータを取り出すことを、 ポップ ( pop ) と言います。 スタックでは、最後に格納したデータが最初に取り出されます。 この仕組みを、 後入れ先出し ( last-in first-out ) と呼びます。
ここで、プログラムについて復習します。
コンピュータが動くことは、コンピュータがプログラムを実行することです。 プログラムは、命令の系列で構成されます。 コンピュータがプログラムを実行することは、CPUがプログラムの命令を次々と解釈して実行することです。
プログラムは、CPUが直接理解できる表現という意味で、一種の言語と考えられます。 この言語を、 機械語 ( machine language ) と呼びます。 プログラムが機械語であることを強調するときは、 機械語プログラム ( machine language program ) という言葉を使います。
CPUが解釈できる命令の全体を、 命令セット ( instruction set ) と呼びます。 一般的に、CPUの種類が異なりますと、命令セットも異なります。 したがって、CPU Aで動くプログラムをCPU Bに与えても、基本的には実行できません。
一般的に、命令は前半部分と後半部分に分かれます。 前半は、命令の種類を表す部分で、 オペレーション・コード ( operation code ) または オペコード ( opcode ) と呼ばれます。 後半は、命令の付加データを表す部分で、 オペランド ( operand ) と呼ばれます。 命令によっては、オペランドのないものもあります。
現在のコンピュータでは、命令はバイトの列で表されます。 プログラムは命令の系列ですので、結局、プログラムはバイトの列となります。
この授業では、Java仮想マシンをCPUと見なして演習を行います。 Java仮想マシン ( Java virtual machine )は、Javaプログラムを実行するために設計された、仮想的なCPUです。 適切なシステムを与えると、現実のCPUは、この仮想的なCPUのシミュレーション(まね)を行うことができます。 実際、javaコマンドは、Java仮想マシンをシミュレーションして、プログラムを実行します。
Java仮想マシンは、基本的には現実のCPUと同じように設計されています。 ただし、いくつかの重要な違いがあります。 今日の話に関係するところでは、Java仮想マシンにはレジスタがないことがあげられます。 Java仮想マシンは、レジスタの代わりに変数とスタックを使います。
それでは、実際にプログラムを作成して実行します。 今日扱う命令は次の通りです。
命令長 | 第1バイト | 第2バイト | 命令 |
---|---|---|---|
1バイト | 0x00 | 何もしない。 | |
1バイト | 0x03 | 整数0をスタックにプッシュする。 | |
1バイト | 0x04 | 整数1をスタックにプッシュする。 | |
2バイト | 0x10 | byte | 整数byteをスタックにプッシュする。 |
2バイト | 0x15 | index | 第index変数の値をスタックにプッシュする。 |
1バイト | 0x1b | 第1変数の値をスタックにプッシュする。 | |
1バイト | 0x1c | 第2変数の値をスタックにプッシュする。 | |
2バイト | 0x36 | index | スタックからポップした値を第index変数に格納する。 |
1バイト | 0x3c | スタックからポップした値を第1変数に格納する。 | |
1バイト | 0x3d | スタックからポップした値を第2変数に格納する。 |
上記の命令には、1バイトのものと2バイトのものがあります。 Java仮想マシンの命令は、第1バイトはオペコード、第2バイト以降はオペランドという形式になっています。
命令の中には、同じ働きをするものがあります。
例えば、命令
10 00
と
03
は、どちらも整数0をスタックにプッシュするものです。
0をスタックにプッシュすることはよく行われますので、特に1バイトの命令が用意されているというわけです。
今日の演習は次のJavaプログラムから始めます。
/* 1*/ class Ex4 { /* 2*/ public static void main (String[] args) { /* 3*/ int x = 6; /* 4*/ int y = 7; /* 5*/ x = y; /* 6*/ System.out.println(x); /* 7*/ } /* 8*/ }
b04a001@AsiaA1:~/comp2l% javac Ex4.java b04a001@AsiaA1:~/comp2l% java Ex4 7 b04a001@AsiaA1:~/comp2l%
バイナリ・ファイルEx4.classを開きますと、下から4行目にプログラム
1006 3c10 073d 1c3c
が見えます。
このプログラムは、上記の3行目から5行目に対応します。
... 00000150: 0010 1006 3c10 073d 1c3c b200 021b b600 ....<..=.<...... ...
このプログラムを読んでみます。
まず、命令
10 06
で整数6がスタックにプッシュされます。
次に、命令
3c
でスタックからポップした値が第1変数(
x
に対応)に格納されます。
この時点で、整数6が第1変数に格納されたことになります。
これは、「整数を第1変数に格納する」という命令がないので、整数をいったんスタックにプッシュし、続いてスタックからポップした値を第1変数に格納しているのです。
続いて、命令
10 07
で整数7がスタックにプッシュされ、命令
3d
でスタックからポップした値が第2変数(
y
に対応)に格納されます。
この時点で、整数7が第2変数に格納されます。
そして、命令
1c
で第2変数の値がスタックにプッシュされ、命令
3c
でスタックからポップした値が第1変数に格納されます。
この時点で、第2変数の値7が第1変数に格納されます。
これも、「第2変数の値を第1変数に格納する」という命令がないので、スタックを仲介にしているのです。
最後に、第1変数の値7が出力されます。
例題1.
プログラム
1008 3d1c 3c00 0000
を実行すると何が出力されるかを答えてください。
出力だけではなく、プログラムの動作についても説明してください。
バイナリ・ファイルEx4.classのプログラムの部分を置き換え、javaコマンドを実行すると、答えが確認できます。
解答例1.
命令
10 08
で整数8がスタックにプッシュされ、命令
3d
でスタックからポップした値が第2変数に格納されるので、第2変数の値は8になる。
命令
1c
で第2変数の値がスタックにプッシュされ、命令
3c
でスタックからポップした値が第1変数に格納されるので、第1変数の値は8になる。
命令
00
が3回で何も変わらない。
したがって、第1変数の値8が出力される。
確認のため、バイナリ・ファイルEx4.classを次のように書き換え、javaコマンドを実行します。
... 00000150: 0010 1008 3d1c 3c00 0000 b200 021b b600 ....=.<......... ...
b04a001@AsiaA1:~/comp2l% java Ex4 8 b04a001@AsiaA1:~/comp2l%
次のプログラムを実行すると何が出力されるかを答えてください。 出力だけではなく、プログラムの動作についても説明してください。 バイナリ・ファイルEx4.classのプログラムの部分を置き換え、javaコマンドを実行すると、答えが確認できます。
1009 3c00 0000 0000
1000 3602 1502 3601
033d 043c 1b3d 1c3c
今日の演習4の答案をメールで提出してください。 メールの差出人は学内のアドレス(b04a001@twcu.ac.jpなど)とし、メールの宛先はkonishi@twcu.ac.jpとします。 メールの本文には、学生番号、氏名、科目名、授業日(10月20日)を明記してください。