プログラミングのおまじない

よくあるのは#include とか。スクリプト系だと#!/usr/bin/hogeとか。includeは何のためにあるのか?というと、Cの場合、私の浅い理解だと、確か…ヘッダファイルをincludeすることによって

  1. シンボルテーブルに外部ライブラリ中にあるシンボル名(関数とか変数の名前)も登録される(登録されない関数は実行時に呼び出すことができない)
  2. リンクをスムーズに行うために、同じヘッダファイルを使うことによってlibc.so等の外部ライブラリのシンボルテーブルと一貫性のあるシンボルテーブルを作成することができる(実際のprintf関数などはlibc.soに書かれているため、libc.soとのリンクが正確に行われないと、そもそもprintfを呼び出せない)。
  3. Cコンパイラがデフォルトパスを持っているため、そのリストに列挙されているパスでファイルをマッチさせることができる(<>だと、デフォルトパスとか-Iから、""だと、カレントディレクトリから)
  4. libc.soとかが持っているシンボル全てをひとつのヘッダファイルに押し込むと膨大な量になってしまうため、いくつかに分割する(膨大な量になると、シンボルのサーチも大変そうだし、何より実行ファイルが大きくなりすぎてしまう)

という効果が得られるから、だったような。

先生、open, read, write, close 関数が同じ低レベル入出力関数に属するのであれば、必要とするヘッダーファイルは1個でいいんじゃないですか?どうして、4種類も指定しなければいけないんですか?どうして open 関数だけ仲間はずれなんですか?関数ごとに違うヘッダーファイルなんて、僕は面倒くさくてやってられません。この際思い切って、printf などもすべて1個のヘッダーファイルにまとめてしまったら、簡単で間違いもないじゃないですか!

いや…しかし…上の私の答えでは、答えになってないような…。こうなってしまったのは、恐らくだけど「中身の仕組みとかどうでもいいから、動けばいい」という思想がどこかにあるのだろうと思う。それは私が今いる会社でもそうなのだが、「入出力をはっきりさせれば、中身はお任せ」という思想がある。つまりそれは「仕様書を渡して仕様通りのソフトウェアが出来上がるのなら、手段は問わない」という思想と同じである。この思想の正当性が担保されるのは、自分たちがほしいソフトウェアをきちんと過不足なく仕様書として表現できて、かつ、ソフトウェアが仕様書になる以前の自分たちの要求条件と、ソフトウェア仕様書と、出来上がったソフトウェアの3品を正確かつ厳密に評価・比較できるというかなり高度な能力があるときだけだ。しかし現実にはそんなに優秀な人間は意外に少ない。だとすると、中身の仕組みもきちんと検証・評価するような物事の考え方が必要なんじゃないかと思う。特にうちの会社に。

  • そもそも、stdio.h というファイルは何を目的としてどこに存在しているのか?
  • #include 文を指定する際には、山括弧(angle brackets)と二重引用符の二種類があるが、両者の使い分けが意味するところは一体何なのか?(一見簡単なようであるが、この問題が意味するところは実に深く、クロス開発において極めて重要な勘所となる)
  • UNIX 環境の場合、通常は /usr/include/stdio.h が選択されるが、ファイル名から絶対パスへの変換は、一体誰がどのような規則に基づいて行っているのか?

(追記)libc.soのシンボルテーブルを覗こうとしたら「シンボルテーブルないよ」って言われた。んなアホな。コンパイラがアドレスを直打ち…してるわきゃないな、と思ったらnmは古いツールだったらしい。a.out形式しか読めないってことなのかもしれない。今時の常識はreadelfみたい。

$ readelf -s /lib/libc.so.xx