ホームに戻る
出典 :
プリプロセッサ | Microsoft Learn プリプロセッサ ディレクティブ | Microsoft Learn #import ディレクティブ (C++) | Microsoft Learn #include ディレクティブ (C/C++) | Microsoft Learn
関連 :
言語バージョンの確認 標準の定義済みマクロ インライン関数 (Inline Function)
目次 :

プリプロセッサディレクティブとは

コンパイルに先立ち、プリプロセッサによるソースファイルの編集が行われる。
プリプロセッサに編集を指示するための語句がプリプロセッサディレクティブである。
以下、標準C/C++におけるディレクティブについて解説する。

マクロの定義に関するもの : #define / #undef

#define SYMBOL01 "VALUE" //< マクロ SYMBOL01 を定義 (値 "VALUE" を割り当て) #define SYMBOL02 //< マクロ SYMBOL02 を定義 (値を割り当てない) #define max(x, y) ( ( (x) > (y) ) ? ( (x) : (y) ) ) //< 関数型マクロ max の定義 void DoSomething1() { string s = SYMBOL01; //< マクロ SYMBOL01 は "VALUE" に置換される int i = max(2, 3); //< 関数型マクロ max(2, 3) は ( ( (2) > (3) ) ? ( (2) : (3) ) ) に置換される : } #undef SYMBOL01 //< マクロ SYMBOL01 の定義を消去 void DoSomething2() { // string s = SYMBOL01; //< SYMBOL01 が定義されていないため、この文はエラーとなる : }
#define はマクロの定義、#undef は定義されたマクロの消去を行う。
上記のコード例では、シンボル SYMBOL01 がマクロとして定義されている。
以降に記述される SYMBOL01 はプリプロセッサによって、SYMBOL01 に割り当てられた値 "VALUE" に置換される。
つまり、DoSomething1() における
string s = SYMBOL01;
はコンパイル開始時点では
string s = "VALUE";
となっている。
SYMBOL02 はマクロとして定義されているが、値は割り当てられていない。記述としては有効で、これは主に後述の条件付きコンパイルで用いる。

関数型マクロ

上記のコード例における max(x, y) は仮引数 x 、y をとる関数形式となっており、関数型マクロと呼ばれる。
通常のマクロと同様にプリプロセッサによって展開される。
実引数には任意の式を用いることができるが、式が正しく展開されるためには上記のように括弧で囲む必要があり、可読性が良くない。
このため、近年ではインライン関数の使用が推奨されている。

条件付きコンパイルに関するもの : #if / #ifdef / #ifndef / #else / #elif / #endif

条件によってコードの有効・無効(コンパイル対象に含めるかどうか)を切り替える際に用いる。

#if / #else / #elif / #endif

#define SYMBOL01 1 //< マクロ SYMBOL01 を定義 (値 1 を割り当て) #define SYMBOL02 //< マクロ SYMBOL02 を定義 (値を割り当てない) #if SYMBOL01 == 1 //< (1) // SYMBOL01 が 1 ならば、ここからのコードは有効 ┓ : (A) // ここまで ┛ #elif SYMBOL01 == 2 //< (2) // SYMBOL01 が 1 でなく 2 ならば、ここからのコードは有効 ┓ : (B) // ここまで ┛ #else //< (3) // SYMBOL01 が 1 でも 2 でもなければ、ここからのコードは有効 ┓ : (C) // ここまで ┛ #endif #if 1 // ここからのコードは有効 : // ここまで #endif #if 0 //< (4) // ここからのコードは無効 ┓ : (D) // ここまで ┛ #endif
#if および #elif (else if に相当)は続く式が真である場合に、次の #elif / #else / #endif までの行を有効とする。
#else は直前の #if または #elif の条件に該当しない場合にのみ、次の #endif までの行を有効とする。
上記の例においては、(1)の SYMBOL01 == 1 が真であれば、(A)のコードが有効となる。
(直前で SYMBOL01 に 1 が割り当てられているため、この式は真となる。)
SYMBOL01 == 1 が偽であれば、続く(2)の SYMBOL01 == 2 が判定され、その結果によって(B)、(C)のいずれかのコードが有効となる。
コード中と同様真は非零、偽は零であるため、(4)のように #if 0 を記述した場合、続くコード(D)は無効となる。
これはコードを削除することなく(一時的に)無効化したい場合に用いられる。

#ifdef / #ifndef

#define SYMBOL02 //< マクロ SYMBOL02 を定義 (値を割り当てない) #ifdef SYMBOL02 // SYMBOL02 が定義されていれば、ここからのコードは有効 : // ここまで #endif #ifndef SYMBOL03 // SYMBOL03 が定義されていなければ、ここからのコードは有効 : // ここまで #endif #if defined SYMBOL02 //< #ifdef SYMBOL02 と等価 : #elif defined SYMBOL03 : #endif #if !defined SYMBOL03 //< #ifndef SYMBOL03 と等価 : #elif !defined SYMBOL02 : #else : #endif
マクロが「定義されているか」(値は考慮しない)を判断する際に用いる。
#ifdef は指定されたシンボルが定義されている場合、#ifndef は定義されていない場合に後続のコードを有効とする。
#if 同様、#else 、#elif と組み合わせることも可能。
また、#ifdef XXX は #if defined XXX
#ifndef XXX は #if !defined XXX と等価である。

ファイルの取り込み : #include

#include <stdio.h> //< (1) #include "hoge.h" //< (2)
ヘッダファイルに記述された型・定数・マクロ定義、外部変数・関数の宣言を取り込むこと(インクルード)ができる。
二重引用符( "" )または山括弧( <> )でファイル名(場合によってはパスを含める必要あり)を指定する。
ここではヘッダファイル stdio.h および hoge.h の内容を、(1)および(2)の箇所に展開する。
尚、拡張子 .c や .cpp のファイルをインクルードすることも可能である。

ファイルの指定方法と検索方式の違い

二重引用符( "" )を用いる場合と山括弧( <> )を用いる場合では、指定されたパスが完全でない場合にプリプロセッサがファイルを検索する順序が異なる。
いずれの場合も、該当するファイルが見つかった時点で検索を停止する。
検索順序の違い
二重引用符
( #include "file-path" )
  1. #include ディレクティブを記述したファイルと同じディレクトリ(カレントディレクトリ)
  2. 1. の親、親の親…と親ディレクトリを順に辿る
  3. コンパイラオプション( /I )で指定されたインクルードディレクトリ
  4. 環境変数 INCLUDE で指定されたパス
山括弧
( #include <file-path> )
  1. コンパイラオプション( /I )で指定されたインクルードディレクトリ
  2. 環境変数 INCLUDE で指定されたパス(コマンドラインでのコンパイル時のみ)
ソースファイルと同階層に配置することが多いユーザ定義のヘッダは二重引用符で、
標準ライブラリなど開発環境から提供されるヘッダは山括弧で指定するのが一般的である。

タイプライブラリの組み込み : #import

出典元を参照。

行番号とファイル名の指定 : #line

定義済みマクロである __FILE__ 、 __LINE__ はその箇所のファイル名および行番号を返すが、#line ディレクティブはこれらを変更することが可能である。
// 本来のファイル名 : line_directive.cpp #include <stdio.h> int main() { printf( "ここはファイル %s%d 行です。\n", __FILE__, __LINE__ ); #line 15 //< #line で行番号を変更 printf( "ここはファイル %s%d 行です。\n", __FILE__, __LINE__ ); #line 30 "hello.cpp" //< #line で行番号とファイル名を変更 printf( "ここはファイル %s%d 行です。\n", __FILE__, __LINE__ ); printf( "ここはファイル %s%d 行です。\n", __FILE__, __LINE__ ); }
実行結果
ここはファイル line_directive.cpp の 7 行です。 ここはファイル line_directive.cpp の 16 行です。 ここはファイル hello.cpp の 31 行です。 ここはファイル hello.cpp の 32 行です。
設定した行番号は、ディレクティブの次行に反映される点に注意。

コンパイルの中断 : #error

#if !defined(__cplusplus) #error C++ compiler required. #endif
#error は指定されたメッセージを出力してコンパイルを終了する。
ここでは、マクロ __cplusplus が定義されていなければ、"C++ compiler required." を出力してコンパイルを終了する(意図的にコンパイルエラーとする)。
コンパイルが継続できない場合に、その原因をプログラマに通知する際に有効。

環境固有のコンパイラ機能呼び出し : #pragma

オペレーティングシステムやコンパイラに固有の機能を呼び出す。プラグマを多用するとコンパイラへの依存が大きくなり、移植性が低下する。
詳細は割愛。