変数のメモリ管理
今回は、プログラムの変数のメモリ管理についてまとめてみる。
ここら辺は、普段プログラムを書いてる人でも、意外と知らないことがあったりするので、理解しておくとちょっと賢くなった気分になれそうですね。
基本データ型
通常のプログラム言語にある基本データ型は以下。
- 整数型
- 実数型
- 文字列型
- 論理型
- ポインタ型(無い言語もある)
初期のプログラム言語では、整数型や実数型が何ビットで表現されるかは、マシンに依存していたが、現在は依存しないようになっている。例えば、Javaでは、以下のように決められている。
byte型 | 8ビット |
short型 | 16ビット |
int型 | 32ビット |
long型 | 64ビット |
float型 | 32ビットの浮動小数点表示 |
double型 | 64ビットの浮動小数点表示 |
配列型
要素の型がTである長さnの1次元配列Aは、T型のデータの表現に使われる記憶域がn個連続したもので表現出来る。
構造体
Pascalのrecord型のデータ、Cのstructを使った構造体などは、その要素(メンバー、フィールドなどと呼ばれる)のデータを1まとめにしたものとして表現される。
struct a { int x; int y; double z; }
また、Pascalのvariant record、Cの共用体(union)は1つの記憶域で異なる型のデータが保持出きるので、それぞれの型の表現に必要な記憶域の最大のもおを取っておくことになる。
union val{ int i_val; float f_val; char *s_val; }v;
クラス
あるクラスを表現するものは、そのクラスに1つしかないものと、そのクラスの各インスタンスに1つずつあるものからなる。前者は、クラス変数、クラスメソッド、インスタンスメソッド、スーパークラスへのポインタ、クラス定数などで、後者はインスタンス変数である。
インスタンスメソッドについて
インスタンスメソッドは、各インスタンスが持っているかのように説明されることがあるが、実際には従来の手続きを、第一引数にインスタンスの情報をつけて呼び出すのと同じである。実際、C++やJavaでもこのように実装されている。(ここら辺の仕組みについては次回やりたい)
- 例
以下のコードは、オブジェクトaにメッセージfと引数bを送ることであり、オブジェクトaがメソッドfを実行すると説明されることが多い。
a.f(b);
しかし、実際は、以下のような手続きをするのと同じ。
f(a, b);
PythonとかPerlをやってる人は、何当たり前の事言ってるんだ?って感じではないだろうか。
インスタンスを第一引数に指定する。習慣としてselfを書くことになっている。
class a: def f(self): ... ...
メソッドの場合、最初の引数を$selfに代入する
package a; ... sub f{ my $self = shift; ... } ...
インスタンス
あるクラスのインスタンスは、図5,もしくは図6のように表現されている
図5の(b)はhandleと呼ばれている。ここで、インスタンスごとの表現はインスタンス変数として宣言されたものである。また、クラスに1つは、クラス変数、メソッド、スーパークラスへのポインタなどである。これは、JVMでは、Classという名前のクラスのオブジェクトとして表現されている。
継承(単一の場合)
単一継承の場合、サブクラスは、スーパークラスの表現の後ろにそのサブクラスで宣言されたものを付け加えた形になる。
- 例
class Point { float x; float y; } class ColoredPoint extends Point { int c; }
上のような場合、そのインスタンスは以下のように表現される。
また、メソッドは、以下のように、あるクラスで使えるメソッド(スーパークラスで定義されているものも含む)の情報をメソッドテーブルとして表現されたりする。
class A { int x; int y; void f(){ ... } void g(){ ... } } class B extends A { int c; void f(){ ... } void h(){ ... } }