「プログラムはなぜ動くのか 第3版」を読みました。
新しく知ったことを備忘録として記載しておきます。
データ型とはどのような種類のデータを格納するかを示すものであり、データ型ごとにメモリを占有するサイズが異なります。
つまり、プログラムで指定するデータ型によって、物理的なメモリーの読み書きサイズを変えることができるようになっています。
例えば、C言語では、char型は1バイト、short型は2バイト、long型は4バイト、double型は8バイトで読み書きします。
ではポインタ型はどのように考えれば良いでしょうか?
例えば、C言語では以下のようにポインタ型を表します。
char *a;
short *b;
long *c;
double *d;
まず、ポインタとはデータが格納されたメモリーのアドレスを持つ変数のことです。
Windowsの場合は基本的に、メモリのアドレスは32ビット(4バイト)か64ビット(8バイト)で表します。
なお、32ビットのWindowsで動くように多くのプログラムはメモリアドレスを32ビットで表すことが多いようです。
つまり、多くのプログラムではポインタ型は4バイトで読み書きします。
では、char *aやshort *bのように、*の前にcharやshortを記載する必要があるのはなぜでしょうか?
それはポインタに格納されたアドレスから一度に何バイトのデータを読み書きするかを示す必要があるからです。
char *aであればアドレスから1バイト読み書きしますし、short *bであればアドレスから2バイト読み書きします。
配列の話もします。
配列はメモリの物理的な構造そのものです。
特にchar型のように1バイトのデータ型の配列は物理的なメモリーの構造と完全に一致します。
1バイトずつ読み書きするのは大変なので、任意のデータ型を指定した配列も宣言できます。
例えば、short型の配列であれば、2バイトずつメモリーを読み書きします。
配列のインデックスを指定することで、順番に読み書きしたり、任意の要素を読み書きしたりできます。
ただ、インデックスを毎回指定するのは面倒くさいので、配列を変形したスタック、キュー、リストなどを使うことが多いです。
PCはソースコードを直接解釈できないので、コンパイルしてネイティブコード(マシン語)に変換する必要があることはご存知かと思います。
このときOSとCPUごとに作成されるネイティブコードが異なることはご存知でしょうか?
OSとは複数のプログラムの集合体ですが、その一つにハードウェア制御プログラムがあります。
各アプリケーションはOSの機能(システムコール)を利用してハードウェアを制御します。
ただし、C言語などの高水準言語ではOSに依存しないように独自の関数名を定義し、コンパイル時に該当するシステムコールを利用するネイティブコードに変換される仕組みになっています。
つまり、OSごとに作成されるネイティブコードが異なります。
そのため、実行環境のOSに合わせてコンパイルする必要があります。
CPUはそのマシン固有のマシン語しか解釈できません。
CPUが異なれば解釈できるマシン語も異なります。
つまり、CPUごとに作成されるネイティブコード(マシン語)が異なります。
そのため、実行環境のCPUに合わせてコンパイルする必要があります。
さいごに「プログラムはなぜ動くのか 第3版」を読んで一番心に刺さった言葉を引用して終わりにします。
コンピュータの世界には、次々と新しい技術が登場していますが、コンピュータにできることが、データを周辺装置から入力する、データをメモリーに記憶する、データをCPUの内部で演算する、そしてデータを周辺装置に出力する、だけであることは変わりません。必然的にプログラムの内容も、突き詰めればデータの入力、記憶、演算、出力だけです。コンピュータもプログラムも、実にシンプルなものなのです。