再び、ポインターとはなにか

このシンボル名と記憶の場所の対応のきびしさを解決するものがポインターだ。ポインターは型の種別であり、したがって式や値の種別であり、変数の種別だ。

変数には本質的なプロパティとして記憶の場所があって、それが評価や代入の時に使用されてプログラムが動くのであった。そして、でも、これらすべては暗黙的に行われていてプログラマーが意識する必要はなかった。

ポインターを使えばこの「記憶の場所」を明示的に、つまり値として扱うことができる。変数 i に対して &i とすればその場所をまさに第一級の値として取り出すことができて、整数と同じように適切な型の変数に入れることもできるし、+ や - なんかの演算子を使って別の値を作り出すこともできる。参照剥がし演算子 * を使えば値である記憶の場所からまた整数の値を取り出すことができる。

はじめに変数は「名札のついた箱」なんて言ったが、malloc を使って名札を付けずに箱だけ用意することもできる。こうして用意した記憶の場所は変数ではないからスコープに寿命を縛られることもない。プログラマーが free するまでいつまでも使える。

こうやってシンボル名のない記憶の場所を表すこともあるから、ポインターは初めに言ったように別の変数への参照に限る、というわけではない。ポインターは記憶の場所の記述だ。そして、この記述を保持する変数がポインター変数ということになる。

ポインター変数もやっぱり変数であることに注意されたい。だから、ポインター変数においても、シンボル名と記憶の場所の対応はやっぱり不変だ。この記憶の場所が、また別の記憶の場所を値として自由に保持できるところがミソなのだ。

だから、ポインター変数が作られると、値が保持される記憶の場所は確保されるが、「値である記憶の場所」が確保されているとは限らない。この値が合法であるときだけ参照剥がしをする責任は、プログラマーに課せられている。

char *p; /* p の記憶の場所は用意されるが、*p の記憶の場所は不正だろう… */