第16回
関数あれこれ

Cは関数の集合

関数についての上述のような規則は、これまでに紹介してきたprintfやgetcharなどのライブラリ関数とまったく同じです。Cではライブラリ関数も独自の関数も、同じように扱われます。

ライブラリ関数も既存の関数も同じ扱い

既に説明してきましたが、Cではプログラマーが独自に作成した関数も、処理系がライブラリにまとめて提供してくれている関数も、基本的にまったく同じ扱いとなります。

Cの言語仕様として備わっている命令はif、for、returnなどの基本的なものだけで、あとは関数を使って処理を組み上げていきます。その関数も、処理系に備わっているものとプログラマーが独自に作ったものとが区別されません。

例えば文字列を表示したい場合、既存のライブラリ関数であるprintfを使わず、代わりに自分の作った関数を使っても構わないのです。

熟練したプログラマーは自作の関数をたくさん持っており、それらに関連したヘッダファイルを作って自在に処理を記述したりします。熟練職人が自分専用の道具を作って愛用するのと似ています。

下請け~孫請け関数

リスト1でmain関数から別の関数を呼び出すという形を紹介しました。呼び出された関数の中から、さらに別の関数を呼び出しても構いません。先の例でも、tateputs関数の中からライブラリ関数のprintfを呼び出しています。プログラマーが独自に作った関数も既存のライブラリ関数も、その扱いに関しては同じです。

このように、ある関数の中から呼び出される別の関数を『下請け関数』などと呼ぶことがあります(図2)。文字通り、部品の生産を下請け工場に委ねるような形です。そのため、下請けの下請けを孫請けなどと呼ぶこともあります。


部品の工場と似た関係

関数呼び出しの様子を、部品の生産にたとえました。機械の部品は大きなユニットからそれを構成する小さな部品へと、下請けに向かって委ねられていきます。そのとき、下請け~孫請けへと部品が小さくなるに従って、生産される部品の汎用性は高くなります(図3)。

例えば、カーナビ(カー・ナビゲーションシステム)を作っている工場は、ボタンや電子回路部分、液晶パネルなどを下請けの会社から購入して製品を組み立てます。電子回路部分を下請けしている工場では、基板や半導体、ビスなどをさらに下請け(孫請け)の工場から購入します。

孫請け工場で作っている半導体やビスは、特定メーカー以外の製品でも、またカーナビ以外の製品でも使えます。電子回路を作っている工場では、やはり別の製品向けの回路も作っているでしょう。


下に行くほど汎用性が高くなる

Cの関数もこれと同じで、下流に行けば行くほど、呼び出される関数の汎用性は高くなります。

その最上流にあるのがmain関数で、いわば本社工場のような存在です。そこから、この例ではtateputsという関数が下請けとして呼び出されますが、汎用性はあまりありません。ただ、この関数の持つ『文字列を縦に表示する』機能は、別のプログラムでも流用できそうです。

さらにtateputsから呼び出されているライブラリ関数のprintfは『書式化された文字列を表示する』というシンプルな機能を持っており、これは特定の目的を持ったプログラムだけではなく、多くのプログラムで利用できるため汎用性は高くなります。

最初に取り上げたパソコンを構成する部品でも同じです。ビデオカードは様々なパソコンに使えますが、パソコン以外の製品には使えません。しかし、ビデオカードに使われている部品類は、ビデオカード以外、パソコン以外の製品にも使えます。

手続き型言語と構造化

Cはいわゆる「手続き型言語」の一種で、命令を組み合わせて作る関数が1つの手続き(プロシージャ)を構成し、さらに関数から関数を呼び出すことで、全体の手続き=プログラムを構成する形になっています。

先の例で示したような下請け~孫請けの構造で、1つのプログラムがどのような下請け処理から構成できるかを考え、さらに1つの関数にどのような処理をさせるかを設計していくことが、Cのプログラミングの出発点となります。

このような形でプログラムを設計していくことを『構造化』と呼びます。うまく合理的な構造を作ることができれば、後のメンテナンス(改良・改造など)もスムーズに進みますが、場合によってはまずい構造を作ることもできてしまいます。

Cのプログラミングは、実はこの構造化の部分が最も大切です。1つの処理をうまい具合に下請け~孫請けに割り振ることができれば、そのテクニックはC++などのオブジェクト指向言語にも役立つはずです。

あとがき

hiropの『ちょっと気になる専門用語』~《関数》

C以外でも、プログラミングでは関数という言葉を非常によく目にします。例えばバージョン6以前のVisualBasicでは、Cの関数に相当する機能はプロシージャですが、ライブラリ関数に相当する処理系に用意された機能が関数と呼ばれます。これは、.NETではあるクラスに備わったメソッドとなります(メソッドは、クラスで定義されたオブジェクトの持つ関数です)。

他の言語でも、数学的な計算処理を行う場合にsin、cos、tanなどの三角関数が使われることはよくあります。関数は英語で"function"──単純に訳すと「機能」となります。

「機能」とは非常に抽象的な言葉ですが、要するに『具体的な処理の内容は分からないけれども、実行した結果は確実に予測できるもの』といったニュアンスです。このような存在を『ブラックボックス(black box)』と言います。真っ黒な箱──外からはその中身が見えない箱──に何か(データ)を放り込むと、一定の処理──箱の外にも明らかになっている処理──を行ってその結果が示される……という仕組みです。

Cに限らず、関数はすべてブラックボックスです。明らかになっている『一定の処理』が『関数の仕様』であり、その関数を呼び出す際には仕様を信じて値を渡します。Cでは、渡す値が『引数』となります。

さて、この「関数」という言葉、昔の数学では『函数』と書きました。『函』とは『箱』のことです。それも、蓋のない箱や誰でも自由に蓋を開けられる箱ではなく、『閉ざされた箱』を意味します。まさにブラックボックスです。

ちなみに、郵便局の私書箱は昔『私書函』、郵便局の前に設置されているポスト(郵便差出箱)は『局函』と呼ばれていたそうです。どちらも鍵がないと開けることのできないブラックボックス(ポストは赤ですが、中は真っ暗)ですね。