
C言語入門|条件付きコンパイルのしくみ
― 二重インクルードを防ぐための基本テクニック
stdio.h って、よく見るけど実体は何なんだろう?
そう思ったこと、ありませんか?
実は stdio.h や stdlib.h は、
ただのテキストファイル です。
エディタで普通に開いて、中身を読むことができます。
そして、その中身をのぞいてみると、
ほぼ必ず次のような構造になっていることに気づきます。

ヘッダファイルの冒頭にある不思議な記述
#ifndef _STDIO_H_
#define _STDIO_H_
/* stdio.h の本体 */
#endif最初に #ifndef と #define、
最後に #endif。
「どうして全部がこんなもので囲まれているんだろう?」
ここに、条件付きコンパイルの重要な役割があります。
二重インクルードとは何か
C言語では、同じヘッダファイルを 2回以上インクルード すると、
中に書かれている宣言や定義が 重複 し、
コンパイルエラーの原因になります。
でも、
「同じファイルを2回も include しないでしょ?」
そう思いますよね。
ところが、実際のプログラムでは簡単に起こります。
二重インクルードが発生する典型例
たとえば次のような構成を考えてみましょう。
- main.c が stdio.h をインクルードしている。
- sub.c も stdio.h をインクルードしている。
- main.c が sub.c をインクルードしている。
この場合、
- main.c → stdio.h
- main.c → sub.c → stdio.h
という経路で、stdio.h が2回取り込まれることになります。
これは標準ライブラリに限らず、
自作のヘッダファイルでも同じです。
図で見る二重インクルードの問題

図の説明
この図では、
- 複数のソースファイルが同じヘッダファイルを参照
- インクルードが連鎖し、同一ヘッダが複数回展開
- 宣言が重複してエラーになる
という流れを視覚的に表しています。
#ifndef と #define による解決策
そこで使われるのが、
条件付きコンパイルを利用した二重インクルード防止です。
基本の書き方は次のとおりです。
#ifndef マクロ名
#define マクロ名
/* ヘッダファイルの中身 */
#endif何をしているのか
| 行 | 意味 |
|---|---|
| #ifndef マクロ名 | マクロが未定義なら続行 |
| #define マクロ名 | マクロを定義する。 |
| 本体 | 初回だけ有効になる。 |
| #endif | 条件付きコンパイルの終了 |
stdio.h の場合の動作イメージ
1回目のインクルード
→ STDIO_H は未定義
→ 本体が有効
→ STDIO_H が定義される
2回目以降のインクルード
→ STDIO_H はすでに定義済み
→ 本体はすべて無効
この仕組みによって、
何回インクルードされても安全
になるわけです。
この方法を インクルードガード と呼びます。
自作ファイルでのインクルードガード例
次は、自作ファイルにインクルードガードを入れた例です。
内容はわかりやすいように少し変更しています。
#include <stdio.h>
#ifndef __SAMPLE_MODULE_C__
#define __SAMPLE_MODULE_C__
void show_message(void)
{
printf("module loaded\n");
}
#endifこの例では、
- SAMPLE_MODULE_C が未定義なら有効
- 2回目以降は中身が無視される
という動作になります。
マクロ名の命名ルールと注意点
インクルードガードのマクロ名には、
ある程度の慣習があります。
| 項目 | 内容 |
|---|---|
| 文字 | すべて大文字 |
| 記号 | ドットはアンダースコアに置換 |
| 装飾 | 前後にアンダースコア2つ |
例
suzuki.c → SUZUKI_C → __SUZUKI_C__注意点
_STDIO_H_ のように
前後がアンダースコア1つの名前 は、
C言語処理系が予約している名前です。
私たちが自作するマクロでは、
必ずアンダースコア2つ を使うようにしましょう。
条件付きコンパイルがここで果たしている役割
この仕組みは、
- 実行時の条件分岐ではない。
- コンパイル前にコードを削除している。
という点が重要です。
二重インクルードの防止は、
条件付きコンパイルの最も代表的で安全な使い道
といえます。
