インクルードガード
二重インクルードを防ぎます。
「インクルードガード」はCoddyKit上の無料C Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C Academyコースには全4レッスンが含まれています。
二重インクルードの問題
ヘッダーが他のヘッダーをインクルードすることはよくあります。そのため、1つのソースファイルに同じヘッダーが2回インクルードされることがあります。
そうなると、ヘッダー内のすべての宣言が2回現れます。structやtypedefなどの型定義では、型を複数回定義できないためコンパイルエラーになります。
どのように発生するのか
a.hとb.hがどちらもtypes.hをインクルードするとします。両方をインクルードするソースファイルでは、types.hが2回貼り付けられた状態になります。
プリプロセッサはヘッダーのテキストをそのままコピーするため、types.hの定義が重複し、再定義エラーが発生します。
/* a.h */ #include "types.h"
/* b.h */ #include "types.h"
/* main.c */
#include "a.h"
#include "b.h" /* types.h now seen twice */インクルードガード
インクルードガードは、ヘッダーを何度も安全にインクルードできるようにする、簡単なプリプロセッサの仕組みです。
ヘッダー全体を条件付きの処理で囲み、本体が最初の1回だけ処理されるようにします。それ以降のインクルードは完全にスキップされます。
#ifndef TYPES_H
#define TYPES_H
/* header contents here */
#endif /* TYPES_H */ガードの仕組み
ヘッダーが最初に読み込まれたとき、TYPES_Hは未定義なので、#ifndefは真になり、本体が処理されます。その後、#defineによってマクロが設定されます。
次回以降のインクルードでは、TYPES_Hがすでに存在するため、#ifndefは偽になり、プリプロセッサは#endifまでスキップします。
#ifndef TYPES_H /* true only the first time */
#define TYPES_H /* remember we've been here */
/* ... body ... */
#endif完全なガード付きヘッダー
ここでは、型定義をガードで保護した実用的なヘッダーを示します。
多くのファイルがpoint.hを直接または間接的にインクルードしても、各翻訳単位でPoint構造体が定義されるのは1回だけです。
/* point.h */
#ifndef POINT_H
#define POINT_H
typedef struct {
int x;
int y;
} Point;
Point point_add(Point a, Point b);
#endifマクロ名の選び方
ガードマクロはプロジェクト全体で一意でなければなりません。そうでないと、2つのヘッダーが衝突し、互いの内容を気付かないうちに隠してしまうことがあります。
ファイル名から大文字で名前を作るのが一般的です。point.hはPOINT_H、net/socket.hはNET_SOCKET_Hになります。
#ifndef NET_SOCKET_H
#define NET_SOCKET_H
/* ... */
#endif予約名を避ける
大文字の後に続くアンダースコアで始まる識別子や、2つのアンダースコアを含む識別子は、コンパイラと標準ライブラリのために予約されています。
__POINT_H__のようなガードは避けてください。POINT_Hのように末尾にアンダースコアを付ける形式は安全で分かりやすい名前です。
#ifndef _POINT_H_ /* risky: reserved name pattern */
#ifndef POINT_H /* preferred */#pragma onceという代替手段
現在の多くのコンパイラは#pragma onceをサポートしています。これはヘッダーの先頭に1行記述するだけで、同じ役割を果たします。
短く書けて名前の衝突も避けられますが、C標準の一部ではありません。従来の#ifndefガードはどの環境でも機能します。
/* point.h */
#pragma once
typedef struct { int x; int y; } Point;ガードとプラグマの比較
#ifndefガードは完全に移植可能で標準的ですが、ヘッダーごとに一意なマクロが必要です。
#pragma onceは簡潔で衝突も起こりませんが、コンパイラのサポートに依存します。安全性を最大限に高めるため、プラグマと従来のガードの両方を使うプロジェクトもあります。
#pragma once
#ifndef POINT_H
#define POINT_H
/* ... */
#endifガードは翻訳単位ごとに機能する
インクルードガードは、ソースファイルをコンパイルする際に、同じソースファイル内での重複インクルードを防ぎます。
ただし、別々の.cファイルでヘッダーがそれぞれ1回ずつ処理されることは防ぎません。これは問題ありません。各ソースファイルは別個の翻訳単位だからです。
習慣にする
作成するすべてのヘッダーに、最初の行からインクルードガードを追加してください。3行分の手間で、分かりにくいエラーの多くを防げます。
単純なヘッダーでも、後から別のヘッダー経由で間接的にインクルードされることがあります。最初からガードを付けておけば、トラブルを避けられます。
/* config.h */
#ifndef CONFIG_H
#define CONFIG_H
#define MAX_USERS 100
#endif理解度チェック
インクルードガードについて、理解度を確認しましょう。
まとめ
ヘッダーを2回インクルードすると再定義エラーが発生する理由と、インクルードガードでそれを防ぐ方法を学びました。
一意のマクロを使って#ifndef/#define/#endifを記述するか、#pragma onceを使います。次は、externを使ってファイル間で変数を共有する方法を見ていきます。
よくある質問
「インクルードガード」レッスンは無料ですか?
はい。「インクルードガード」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C Academyコースには全4レッスンが含まれています。
「インクルードガード」で何を学びますか?
二重インクルードを防ぎます。 ブラウザで直接実行するハンズオンコードでC Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
C Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのC Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「インクルードガード」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このC Academyレッスンでコードを書いて実行できますか?
はい。すべてのC Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。