| データだけでなく、プログラム自体もメモリの中にあります。プログラムが実行されているときに、メモリがどんな状態になっているのか想像してみましょう。プログラムは、命令の集まりです。命令は、基本的には実行される順番に並んでいます。ちょうど次の図のように命令が並んでいます。 | ||
![]() |
||
| コンピューターの命令を実行するのは CPU です。CPU の中には、現在使っている値を保持したり、いろいろな演算を行うための特別な記憶領域があって、これをレジスタと呼びます。ものすごく小さなメモリだと思えば良いでしょう。例えばメモリ上のアドレス a と b から取り出した値を足し合わせてアドレス c に書き戻したりするときに、値を一時的にレジスタに格納します。 | ||
| CPU の中にはさらに PC(Program Counter) と呼ばれる特別な記憶領域があります(もう一度上の図を見てください)。ここには、プログラムの中のどの部分が現在実行中かを表すために、今から実行しようとしている命令が格納されているアドレスが入っています。「命令1」を実行するときにはそのアドレスが格納されていて、「命令1」の実行が終わると次に進むために PC の値を1命令分だけ進めます。すると、今度は「命令2」が実行されて、また PC の値が1つ進みます。 | ||
| そういうわけで、関数を作るとそれを実行するための命令がひとかたまりになってメモリ上のどこかに配置されます。例えば次のような関数 func があったとしましょう。 | ||
void
|
||
| すると、メモリ上ではだいたい次の図のような感じになります。 | ||
![]() |
||
| さて、メモリ上にある情報はどんなものであれアドレスを持っていますから、そのアドレスを格納するためのポインタ変数を作ることができます。それは関数でも同じことです。関数のアドレスを格納するポインタには特別な名前がついていて、「関数ポインタ」と言います(それほど特別という感じはしない名前ですが)。 | ||
| ところで関数のアドレスとはどこでしょうか?答えを先に言うと、関数の先頭です。つまり、関数の中で最初に実行される命令が入っているアドレスです(厳密にはマシンやコンパイラによって違うかもしれませんが、そう理解してさしつかえありません)。これはちょうど、int 型変数のアドレスをその先頭バイトのアドレスで表したり、文字列(これは char 型の配列でしたね)のアドレスを先頭の文字を表す char 型変数のアドレスで表したりするのと似ています。関数のアドレスを表す値が実際にはどんな数値なのかを知る必要はありません。これは関数名を使って表します。たとえば func という名前の関数があったら、func という識別子がそのアドレスを表すラベルになります。 | ||
| 関数 func のポインタ pFunc は次のように宣言できます(文法については詳しく触れませんが、よくわからなければ解説書を見てくださいね)。 | ||
|
||
| メモリ上では次の図のような感じです。 | ||
![]() |
||
| 関数の実行を開始する位置のことを「エントリポイント」と呼びます(ただ訳しただけですけどね)。ですから、関数名(すなわち関数のアドレス)とはエントリポイントのことです。関数の途中の命令が入っているアドレスを知ってもほとんど意味がないですから、エントリポイントさえあれば十分です。 | ||
| せっかく関数ポインタを覚えたので、これを無理矢理使って "Hello, world" を書いてみましょう。 | ||
|
||
| pPuts が関数ポインタです。puts のプロトタイプは | ||
|
||
| ですから、上のプログラムのような宣言になります。関数ポインタを使って関数を呼び出すには、関数と同じように書けば良いのです。このとき puts 関数の本体と関数ポインタ pPuts がメモリ上でどのようになっているのかというと、次の図のようになっています。 | ||
![]() |
||
| 簡単でしょう?でもこの例題は puts を間接的に呼んでいるだけで、なんだか馬鹿馬鹿しいですね。そこで次回は、もっと実用的な関数ポインタの使い方を説明することにします。 | ||
("Hello, ANOTHER world!" は2001年11月から2002年11月にかけて作成されたコンテンツです。)