ショートカット
ファシリテーター × あり方
コーディングの向こう側
Hello, ANOTHER world!
オブジェクト指向のはなし
プログラミングのはなし
C言語実力診断クイズ
eSkillBooks
Hello, ANOTHER world!

パラメータの受け渡し

データの構造を取り決めることによって、パラメータが関数の間を行き来できるということを前回説明しました。関数呼び出しの時、どのようにパラメータが渡されるのか見てみましょう。
まず、呼び出す側を main、呼び出される側を sub と名付けましょう。このとき、両者が想定するパラメータのデータ構造が同じであるということが重要です。そうでないと、パラメータを受け渡すことができません。main は sub を呼び出す前に、パラメータを準備します。メモリ上にパラメータとなるデータを格納するための領域を確保し、そこに実際のデータを書き込みます。
次に、main は sub に制御を移します。このとき、確保したパラメータのアドレスを sub に渡すようにします。すると sub は main が準備したパラメータにアクセスすることができます。
さて、"Hello, world" の場合はパラメータが文字列ですから、事態はもう少し複雑になります。puts 関数を使って文字列を表示する場合を考えて見ましょう。main はまず文字列をメモリ上に確保し、さらに sub に渡すためのポインタをメモリ上に確保し、そのポインタに文字列のアドレスを格納します。そしてポインタのアドレスを渡しつつ puts を呼び出します。
puts はパラメータの構造を知っているので、受け取ったデータを適切に処理できます。
単純な話ですね。
でも、これだけではうまくいきません。呼び出す側と呼び出される側が共通のデータ構造を知っている必要がありますが、パラメータのデータ構造は無数にあるので、普通にやっていては毎回呼び出しのルールが変わってしまいます。これでは効率が悪くてどうしようもありません。パラメータをいくつも渡すような関数もたくさんあります。そこで、関数を呼び出すときの共通の枠組みが必要になります。そういうルールのことを、関数の「呼び出し規約」と言います。
実際に関数を呼び出すときには、「スタック」という構造を使います。パラメータを収めるための、共通の器のようなものです。呼び出す側はスタックにパラメータを入れ、呼び出された側はスタックに入れられたデータを読み出します。
次のようなプロトタイプの関数
void sub( int x, int y );
を呼び出す場合を考えてみましょう。main はプロトタイプに従って、まず x をスタックに入れます。その次に y を入れます。スタックの中身は入れたデータが下から順に積み重なるような感じになります。「スタック(stack)」とは「積み重なる」という意味です。それから sub を呼び出します。sub は自身のプロトタイプに従ってスタック中の x, y にアクセスすることができます。
この図は必ずしも正確ではありません。呼び出し規約はプラットフォームや言語によって変わるので、ここでは概念だけ図解しています。
このような呼び出しは、スタックを使ってデータを受け渡すという一般化された仕組みと、sub のプロトタイプを main が知っているということによって実現されています。もし main が sub のプロトタイプを知らなければ、正しくパラメータを渡すことはできません。コンパイラは、呼び出す側が呼び出される側のプロトタイプに従って正しくパラメータを指定しているかどうかチェックしています(パラメータの型を間違えたときにエラーや警告が出るのはそのためです)。このチェックがないと呼び出しの際に重大な不整合が起こり、リンクエラーになるか、プログラムがクラッシュすることになります。プロトタイプはとても重要なのです。
実は、古いコンパイラにはこのようなプロトタイプのチェックがありませんでした(パラメータの型を間違えても警告が出ませんでした)。それで関数呼び出しはとても慎重に行う必要がありました。最近のコンパイラはきちんとチェックがあるので、プロトタイプを正しく書いておけば呼び出しの間違いを防ぐことができるようになっています。また、puts などの標準関数については include ファイルの中にプロトタイプが記述されています。それで、include さえ適切に行えばコンパイラがパラメータの間違いに対して警告を出してくれるので、とくに面倒なことをしなくても正確な呼び出しができます。include の重要な目的のひとつは、プロトタイプをコンパイラに教え、関数呼び出しのケアレスミスを未然に防ぐことなのです。
ところで、スタックはパラメータを格納するためだけに存在するのではありません。次回は関数呼び出しとスタックとの間の別の関係について説明します。

("Hello, ANOTHER world!" は2001年11月から2002年11月にかけて作成されたコンテンツです。)