第14回
ヘッダファイルとプリプロセッサ指令

ヘッダファイルとプリプロセッサ

ヘッダファイルには、ソース中で用いる関数など様々な定義が記述されています。それを読み取ってコンパイラに引き渡すのがプリプロセッサです。

ヘッダファイルの中身

#include指令はそれが記述された箇所に指定したファイルを読み込んで展開する命令で、一般には拡張子.hのヘッダファイルが指定されます。Cで最も有名(?)なヘッダファイルは、おそらく“stdio.h”でしょう。stdioはstandard input/outputの略で、標準入出力に関する関数や定数、構造体 ※1 などの定義を行っています。

ここで、stdio.hの中身を少し覗いてみましょう。基本的にはどの処理系でもほぼ同じような定義が記述されていますが、書き方は処理系によって異なります。リスト1はLSI-C 86に用意されているstdio.hの一部です。

型の異なる複数の変数を1つにまとめて名前を付けたもの。詳しくは回を追って紹介します
リスト1:stdio.hの一部(LSI-C 86の場合)
      :
#ifndef	__STDIO_H
#define	__STDIO_H

#ifndef	ANSI
#include <stddef.h>	/* for compatibility to older version */
#endif	/* ANSI */

#ifndef	__SIZE_T_DEFINED
#define	__SIZE_T_DEFINED
typedef	unsigned size_t;
#endif

#ifndef	__VA_LIST_DEFINED
#define	__VA_LIST_DEFINED
typedef	void	*va_list;
#endif

#ifndef	__FPOS_T_DEFINED
#define	__FPOS_T_DEFINED
typedef	long	fpos_t;
#endif

#ifndef	__FILE_DEFINED
#define	__FILE_DEFINED
typedef	struct	{
	char	mode;		/*	file mode	R, W, R/W	*/
	char	*ptr;		/*	next character position		*/
	int	rcount;		/*	number of characters left	*/
	int	wcount;		/*	number of rooms left		*/
	char	*base;		/*	location of buffer		*/
	unsigned bufsiz;	/*	size of bufer			*/
	int	fd;		/*	file descriptor			*/
	char	smallbuf[1];	/*	used for buffer when unbufferd	*/
}	FILE;
#endif
      :

ヘッダファイルは#の連続

リスト1をよく見てみると、ほとんどの行が#で始まっています。stdio.hは単純に様々な定義を行っているわけではありません。Cの処理系には他にもたくさんのヘッダファイルが用意されており、1つのソースに複数のヘッダファイルが取り込まれることはよくあります。

それらヘッダファイルには、同じ名前の識別子(シンボル)が重複して定義されていることがあります。例えば記号定数のNULLは、標準入出力関数だけではなく文字列を扱う関数でも使われるため、文字列処理の関数を宣言しているstring.h内でも定義しておく必要があります。

そこで、記号定数の定義などで重複を防ぐため、
もし定数"xxxx"が定義されていなければ
"xxxx"を"ZZZZ"という名前で定義せよ
といった形の記述をします。ヘッダファイルが取り込まれる順序は一定ではないため、処理系に準備されているすべてのヘッダファイルで、このような定義の重複に対処する記述がされています。

プリプロセッサの制御構造

例えばリスト1には、以下のような記述があります。
#ifndef	__STDIO_H
#define	__STDIO_H
#ifdefは「もし、続く記号定数が定義(define)されていなければ、続く指令を処理せよ」という意味です。#defineは本来以下のような書式で、記号定数を定義します。

#define <識別子名> <値>
<値>を省略すると1とみなされます。従って上の2行は次のような意味になります。

もし"__STDIO_H"が定義されていなければ
"__STDIO_H"を"1"と定義せよ
この定義は、取り込まれた他のヘッダファイルが、既にstdio.hが取り込まれているかどうかを判断する際に用いられます。stdio.hが取り込まれていれば記号定数“__STDIO_H”が1と定義されているので、他のヘッダファイルで

#ifndef	__STDIO_H
という行を記述すれば、そのことが判別できます。

このようにプリプロセッサ指令では、プリプロセッサの動作そのものに対して制御構造を記述できます。