Categories Books | Hard | Hardware | Linux | MCU | Misc | Publish | Radio | Repository | Thoughts | Time | UNIX | Writing | プロフィール
manual pages の構成や内容は、オペレーティングシステムによりかなり様相が異なりますが、その背景にある設計者の思想を理解するためには、Cソースファイル中で頻回に登場する「ヘッダーファイル」の意味を正確に把握しておく必要があります。
残念なことに、ヘッダーファイルの取り扱いは、動作環境や処理系によって影響を受けることもあり、従来のC言語入門書や UNIX システムプログラミング解説書で深く追求されることはありませんでした。
printf 関数を使用する時は、プログラムの先頭に #include <stdio.h> と書く。
C言語入門書中で必ず登場するこの一文ですが、その意味が語られることはまずありません。向学心旺盛な読者であれば、#include <stdio.h> が意味するところを知りたいと思うはずですが、この大切な疑問を解いてくれる情報は、書籍やインターネット上には存在しないのです。結果として、彼らは #include <stdio.h> を単なる「呪文」として暗記するしかなく、成長の芽は摘まれてしまいます。
"#include <stdio.h>" という表記は、いくつかの重要な疑問を私達に投げかけています。
私は、この3つの問いに答えてくれる書籍や記事を求めて、長い間探索を続けましたが、結局見つけることはできませんでした。自力で答えを探り当てるしか、なかったのです。答えは、拙著 "GNU 開発ツール" に記してありますが、かっての私のように「#include の意味が分からない」と真剣にお悩みの方は、本書を読むことで心のわだかまりが溶けることでしょう。
この「stdio.h 問題」については、今後書籍の解説とはまた違った視点から、簡単に取り上げてみます。
さて、一般的な教科書では、標準入出力関数の説明が一通り終わると、低レベル入出力関数(システムコールと呼ばれることもあるが、この表記は一部の鋭い読者に誤解をもたらす原因となっている)の解説へと移ります。定番の open, read, write, close 関数が登場しますが、コーディング上の解説はおおよそ次の通りです。
低レベル入出力関数を用いて、ファイルを開くときは open 関数を使うが、この時は #include <sys/types.h>, #include <sys/stat.h>, #include <fcntl.h> の3行を追加する。ファイルの中身を読み書きする read, write 関数、およびファイル操作を終了する際に呼び出す close 関数を使用する時は、#include <unistd.h> を指定すること。
おそらく、ほとんどの初心者はこの段階で「???」の状態に陥っているのではないでしょうか。もしくは、日本の教育現場において教科書や授業の内容に疑問を差し挟むことは「悪」とされる風潮があるため、読者は頭の隅に湧きかけた疑問を封印し「そういうものなのだ」と自らを納得させているのかもしれません。
ここで、勇気ある少年A君がシステムプログラミングの講義中に次のような質問を行ったとしましょう。
先生、open, read, write, close 関数が同じ低レベル入出力関数に属するのであれば、必要とするヘッダーファイルは1個でいいんじゃないですか?どうして、4種類も指定しなければいけないんですか?どうして open 関数だけ仲間はずれなんですか?関数ごとに違うヘッダーファイルなんて、僕は面倒くさくてやってられません。この際思い切って、printf などもすべて1個のヘッダーファイルにまとめてしまったら、簡単で間違いもないじゃないですか!
果たして、先生は何と答えるのでしょうか。「屁理屈言わずに、教科書に書かれている通りコーディングせんと、ちゃんとしたプログラマには、なれへんど!」と怒り飛ばすのか、それとも「君は将来が楽しみな奴やな!」と賞賛するのか。
実は、Linux/GNU, *BSD 環境は前者の先生タイプなのですね。つまり、世の中のほとんどの先生は、A君の疑問に対して否定の立場を取ります。しかし、たったひとつだけ「君の疑問はもっともなのだよ」と理解を示し、自らが範を示す環境があるのです。
「理解ある先生」の正体はもうしばらく伏せておくとして、次回は実際にコーディングを通して、Linux/GNU, *BSD 環境におけるヘッダーファイルの取り扱いを見てみましょう。
続く