C言語のインクルードガードはpragma onceを使う
インクルードガードとは、ソースコードのinclude処理が重複して発生することを防ぐためのものです。C言語では、define/ifndefのプリプロセッサディレクティブを利用して、インクルードガードを実現していました。処理系の拡張により pragma once が追加されました。
読み方
- pragma once
- ぷらぐま わんす
- インクルードガード
- いんくるーどがーど
- include guard
- いんくるーどがーど
目次
概要
C言語のプリプロセッサ(cpp) は、プリプロッサディレクティブ の include が出現するたびに、ヘッダファイル(.h)を展開しようとします。
インクルードガードは、ヘッダファイルの相互インクルードで無限ループに陥ることを防ぐことができます。ただし、defineの重複を排除することができないため、思わぬ副作用を産んでしまうこともありました。
何度もインクルードすることによって、定義が重複してしまいますが、インクルードガードがあれば、定義を一度にすることができ、コンパイル時の処理量を減らすこともできます。
ここでは、ifndef/define のインクルードガードよりも便利になった pragma once を紹介します。
古いインクルードガードの実装例
古い時代では、インクルードガードは、以下の ifndef/define のプリプロセッサディレクティブを利用して実現していました。
/* * include_guard.h * Copyright (C) 2016 kaoru <kaoru@localhost> */ #ifndef INCLUDE_GUARD_H #define INCLUDE_GUARD_H void foo(void); #endif /* !INCLUDE_GUARD_H */
これは、ちょっと、書くのが面倒ですね。
defineを利用した方法の問題点として、define が被ってしまうと、インクルードガードで定義が読み込めないといった副作用を及ぼし、コンパイルできないといった事象を引き起こすこともあります。宣言しているのに、宣言されたことにならない、といった場合は、defineの衝突を調査したほうが良いでしょう。
ソースコード
pragma_once.h
pragma once を利用した場合には、以下のようになります。
/* * pragma_once.h * Copyright (C) 2016 kaoru <kaoru@localhost> */ #pragma once void foo(void);
ifndef/define を利用していたときに比べて、コードが簡素になりました。これならdefineの衝突の心配もありません。
main.c
/* * main.c * Copyright (C) 2016 kaoru <kaoru@localhost> */ #include "pragma_once.h" #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { foo(); exit(EXIT_SUCCESS); } void foo(void) { puts("foo"); }
コンパイル
$ cc -I. -std=c99 main.c
実行例
$ ./a.out foo
コンパイラごとのパフォーマンス
Cコンパイラ によって、インクルードガードは ifndef 形式が速い場合もあれば、pragma once が速いこともあるし、どちらもそんなに変わらないケースもあります。
まとめ
- インクルードガードとは、includeディレクティブによる相互インクルードの無限ループを防ぐために利用されます。
- C言語では、インクルードガードは pragma once を利用します。
- define の衝突のリスクがありません。
- ヘッダファイルを作成する場合は、無限ループを防ぐために、インクルードガードを記載します。
- メジャーな処理系では、対応していますが、もし、非常に古いコンパイラを使う場合は、機能しないかもしれません。
関連項目
ツイート