おまじないではない#include

C言語を最初に学ぶと、必ず出てくる以下の行

#include <stdio.h>

おなじないと言われて、常に書いている人がいるみたいだが、おまじないでもなんでもなくて stdio.h ファイルをそこに展開せよといっているだけである。

なんで展開するかというと、たいていはprintf関数を使うので、そのプロトタイプ宣言なんかが書いてあって必要になるから。使う分にはそれでもいいけど、それだと面白くないからstdio.hファイルでも見てみようかと思うわけですよ。

どこにあるかというと、まぁ、環境によるわけですが、UNIX系の場合 /usr/include だったりします。 というわけで、/usr/include/stdio.hファイルを見ていけばいいということで。以下、FreeBSD 7.3-RELEASE の環境の話です。

ファイル長いので、面白そうなとこだけ見ていこうかと。
最初は以下の通り。なお、先頭の番号は行番号。

    40  #ifndef _STDIO_H_
    41  #define _STDIO_H_
    42
    43  #include <sys/cdefs.h>
    44  #include <sys/_null.h>
    45  #include <sys/_types.h>

いきなり、意味分からん書き方。_STDIO_H_ が定義されていなかったら定義する、ってどういうこっちゃ。これ、このファイルを読み込んだら _STDIO_H_ を定義する、ただし、別の箇所で再度#include されるかもしれんから、そのときは、スルーしてよってこと。#define なんかでいろいろ定義するから、2重読み込みされると、いろいろ困るんだよね。もちろん、#endif はファイルの最終行にあります。

で、この定義名の命名規則を決めておくと、どのファイルを読み込んだかも分かるっていうトリッキーなやりかた。この場合、ファイル名の両端に_を付けて.を_にし、大文字にするという規則。

さて、次の行から三連ちゃんで別のファイルをincludeしている。実体は /usr/include/sys/cdefs.h、/usr/include/sys/_null.h、/usr/include/sys/_types.h にある。
cdefs.hは、まぁ、内部的に使ういろんなのが#define されてます。
_null.h は、そのままで NULL の定義が書いてます。短いのでみてみると、

    29  #ifndef NULL
    30
    31  #if !defined(__cplusplus)
    32  #define NULL    ((void *)0)
    33  #else
    34  #if defined(__GNUG__) && defined(__GNUC__) && __GNUC__ >= 4
    35  #define NULL    __null
    36  #else
    37  #if defined(__LP64__)
    38  #define NULL    (0L)
    39  #else
    40  #define NULL    0
    41  #endif  /* __LP64__ */
    42  #endif  /* __GNUG__ */
    43  #endif  /* !__cplusplus */
    44
    45  #endif

31行目でC++かどうかの判定ですね。そうでなければ(C言語の場合)、NULLは (void*)0 なわけです。そうですね、C言語ではNULLはポインタなんですよね。だからポインタへのキャストしているわけですね。C言語でvoid *はなんでもOKなポインタだから出来る技になっております。C++の場合、少しやっかいになって、void *の扱いが変わるためいろいろ面倒なことになっているんですね。だから、0を特別なポインタとして扱うようにして、まぁ、なんとかやってるわけです。

つづきはまた今度