C言語/前処理指令

出典: フリー教科書『ウィキブックス(Wikibooks)』
ナビゲーションに移動 検索に移動

このページでは前処理指令を網羅したが、 よく使われるのは、「インクルードガード」または「#paragma once」、「ソースファイル取り込み」、「マクロ置き換え」くらいで、 他は読み飛ばしてもかまわないだろう。

前処理指令の基本[編集]

前処理指令とは、#前処理字句で始まり、それに続く最初の改行文字で終わる一連の前処理字句である。 前処理指令は、翻訳フェーズの(4)において実行される。 [1] 前処理指令によって、 ソースファイルの一部分を条件によって処理したり読み飛ばしたり、 他のソースファイルを組み込んだり、 マクロを置き換えたりすることができる。 これらの処理は、概念的にはその翻訳単位の翻訳の前に行うので、前処理と呼ぶ。

条件付き取り込み[編集]

条件付き取り込みとは、ソースファイルの一部分を条件によって処理したり読み飛ばしたりする前処理指令である。

条件付き取り込みの記述は次のようになっている。

#if 定数式1
グループ1
#elif 定数式2
グループ2
#else
グループ3
#endif

 #if,#elif,#elseは、それぞれ制御文のif,else if,elseと同様の使い方をする。 すなわち、定数式1が真(0以外)の場合グループ1が実行され、 それ以外の場合で定数式2が真(0以外)の場合グループ2が実行され、 さらにそれ以外の場合グループ3が実行される。

定数式では次の形式の単項演算子式を含んでもよい。

defined 識別子
defined (識別子)

defined単項演算子は、 識別子がその時点でマクロ名として定義している場合1と評価し、 そうでない場合0と評価する。

また、次の形式の前処理指令もある。

#ifdef 識別子
グループ
#endif

これは次の前処理指令と等価である。

#if defined 識別子
グループ
#endif
#ifndef 識別子
グループ
#endif

これは次の前処理指令と等価である。

#if !defined 識別子
グループ
#endif

[2]

インクルードガード[編集]

インクルードガードとは、複数回ヘッダファイルをインクルードすることを防止する方法である。 インクルードガードの記述は次のようになっている。

#ifndef インクルードガード用の識別子
#define インクルードガード用の識別子
/*
ソースコード
*/
#endif

このヘッダファイルをインクルードする際、 2回目以降は「インクルードガード用の識別子」が定義されているため、 ソースコードが読み飛ばされる。

また、コンパイラによっては次のようなインクルードガードの記述もある。

#pragma once

ソースファイル取り込み[編集]

ソースファイル取り込みとは、他のソースファイルを取り込む前処理指令である。

ソースファイル取り込みの記述は次のようになっている。

#include <h文字列>
又は
#include "q文字列"

 #indlude前処理指令は、 h文字列ではヘッダを、q文字列ではソースファイルをそれぞれ取り込む。 「#include "q文字列"」の探索をサポートしていない場合や、探索が失敗した場合は、 同じ文字列を「#include <h文字列>」と読み替えたのと同じ規則で再処理する。 [3]

マクロ置き換え[編集]

マクロ置き換えとは、マクロ名を置換要素並びで置き換える前処理指令である。 マクロ置き換えには、オブジェクト形式マクロと関数形式マクロがある。

  • オブジェクト形式マクロ

次の形式の前処理指令をオブジェクト形式マクロと呼ぶ。

#define 識別子 置換要素並び

defineの直後に記述してある識別子をマクロ名という。 オブジェクト形式マクロは、 これ以降のマクロ名を置換要素並びで置き換える。

  • 関数形式マクロ

次の形式の前処理指令を関数形式マクロと呼ぶ。

#define 識別子(識別子並び) 置換要素並び

識別子並びは省略可能で、仮引数を指定し、個々の仮引数は「,」で区切り、最後に省略記号「...」を指定してもよい。 関数形式マクロは、 これ以降のマクロ名とそれに続く「(」からそれに対応する「)」までを、 置換要素並びで置き換える(マクロ呼出し)。

[4]

実引数置換[編集]

実引数置換とは、関数形式マクロの実引数を識別した後行われ、 実引数の中に含まれるすべてのマクロの展開後、 置換要素並びの中の仮引数を対応する実引数で置き換える。 ただし、#前処理字句または##前処理字句が前にある仮引数と、##前処理字句が後に続く仮引数とは除く。 実引数置換の前に、実引数の前処理字句列を完全にマクロ置き換えする。 このとき、実引数ごとに、その前処理字句列が、現在の前処理ファイルの残りとなっているものとみなして処理する。 つまり、他の前処理字句列が存在しないかのように扱われる。

置換要素並びの中に現れる識別子__VA_ARGS__は、それが一つの仮引数であるかのように扱われなければならない。 それを、可変個数の実引数が形成する前処理字句列で置き換える。

[5]

#演算子[編集]

 #演算子は、文字列化演算子と呼ばれ、 関数形式マクロの置換要素並びの中の仮引数の直前に付けることができ、

  1. 前処理字句と仮引数を、実引数を「"」で囲んだ単純文字列リテラルで置き換える。

その際、「"」文字及び「\」文字の前に、「\」文字が挿入される。 [6]

##演算子[編集]

 ##演算子は、トークン連結演算子と呼ばれ、 オブジェクト形式マクロ又は関数形式マクロの置換要素並びの中で使われ、 その直前にある字句とその直後にある字句を連結する。 関数形式マクロの置換要素並びの中で、 仮引数の直前又は直後に##前処理字句がある場合、 その仮引数を対応する実引数で置き換える。 [7]

再走査と再置き換え[編集]

実引数置換、#演算子及び##演算子の処理、プレースマーカー前処理字句の削除を行った後、 さらに置き換えるべきマクロ名があるかどうかを調べるために、 ソースファイル上のその後のすべての前処理字句とともにその結果の前処理字句を再走査する。

現在置き換え中のマクロの名前を置換要素並びのこの走査中に検出した場合や 入れ子になった置き換えで現在置き換え中のマクロの名前を検出した場合は、 置き換えは行わない。

完全にマクロ置き換えした結果の前処理字句列は、 前処理指令の形をしていたとしても、 前処理指令として処理することはない。

[8]

マクロ定義の有効範囲[編集]

マクロ定義の有効範囲は、そのマクロ定義から、対応する#undef指令を検出するまで又は前処理翻訳単位の最後までである。

マクロ定義を無効にするには、次のように記述する。

#undef 識別子

識別子で無効にしたいマクロ名を指定する。

[9]

行制御[編集]

行制御とは、この指令の次のソース行の行番号を指定する前処理指令である。 行制御の記述は、次の2通りがある。

#line 数字列

この指令の次のソース行の行番号が、数字列で指定された行番号となる。 数字列は1以上2147483647以下の10進整数でなければならない。

#line 数字列 "s文字列"

この指令は、同様に行番号を設定し、s文字列でソースファイル名を置き換える。

また、次の形式の行制御の前処理指令もある。

#line 前処理字句列

この形式の前処理字句列は、 マクロ置き換えで、上の2通りの形式のいずれかと一致せねばならず、 一致した形式の規則に従って処理する。

[10]

エラー指令[編集]

エラー指令とは、処理系に対し、 指定された前処理字句の列を含む診断メッセージを出力することを指示する前処理指令である。 エラー指令の記述は次のようになっている。

#error 前処理字句列

前処理字句列で出力する診断メッセージを指示する。 [11]

プラグマ指令[編集]

プラグマ指令とは、処理系に対し処理系定義の方法で動作することを指示する前処理指令である。 処理系が認識できないプラグマ指令は、無視する。 プラグマ指令の記述は次のようになっている。

#pragma 前処理字句列

[12]

詳しくはコンパイラのヘルプを参照せよ。

空指令[編集]

空指令とは、何の効果もない前処理指令である。 空指令の記述は次のようになっている。

#

[13]

あらかじめ定義されたマクロ名[編集]

あらかじめ定義されたマクロ名がある。 そのマクロ名と意味は次のようになっている。

マクロ名 意味
__DATE__ 前処理翻訳単位の翻訳の日付("Mmm dd yyyy"の形式をもつ単純文字列リテラル)
__FILE__ ソースファイル名(単純文字列リテラル)
__LINE__ 現在のソース行の行番号(整数定数)
__STDC__ 整数定数1.規格合致処理系であることを示す。
__STDC_HOSTED__ 処理系が規格合致ホスト処理系の場合、整数定数1。そうでない場合、整数定数0。
__STDC_VERSION__ 整数定数199901L。
__TIME__ 前処理翻訳単位の翻訳の時刻。("hh:mm:ss"の形式の単純文字列リテラル)
__STDC_IEC_559__ 「IEC60559 浮動小数点演算」の規定に合致することを示すための整数定数1。
__STDC_IEC_559_COMPLEX__ 「IEC60559 互換複素数演算」の規定に合致することを示すための整数定数1。
__STDC_ISO_10646__ 型wchar_tの値が、ISO/IEC 10646で定義された文字の符号化表現をもつことを示すためのyyyymmLの形式の整数定数。

[14]

プラグマ演算子[編集]

プラグマ演算子は、指定した文字列リテラルを文字列解除し、その結果をプラグマ指令の中の前処理字句列として実行する。 つまり、次の形式の指令は、その次の形式でも表現できる。

#pragma listing on "..\listing.dir"
_Pragma("listing on \"..\\listing.dir\"")

[15]

脚注[編集]

  1. ^ 『JISX3010:2003』p.110「6.10 前処理指令」
  2. ^ 『JISX3010:2003』p.112「6.10.1 条件付き取り込み」
  3. ^ 『JISX3010:2003』p.113「6.10.2 ソースファイル取り込み」
  4. ^ 『JISX3010:2003』p.114「6.10.3 マクロ置き換え」
  5. ^ 『JISX3010:2003』p.116「6.10.3.1 実引数置換」
  6. ^ 『JISX3010:2003』p.116「6.10.3.2 #演算子」
  7. ^ 『JISX3010:2003』p.116「6.10.3.3 ##演算子」
  8. ^ 『JISX3010:2003』p.117「6.10.3.4 再走査と再置き換え」
  9. ^ 『JISX3010:2003』p.117「6.10.3.5 マクロ定義の有効範囲」
  10. ^ 『JISX3010:2003』p.120「6.10.4 行制御」
  11. ^ 『JISX3010:2003』p.121「6.10.5 エラー指令」
  12. ^ 『JISX3010:2003』p.121「6.10.6 プラグマ指令」
  13. ^ 『JISX3010:2003』p.121「6.10.7 空指令」
  14. ^ 『JISX3010:2003』p.121「6.10.8 あらかじめ定義されたマクロ名」
  15. ^ 『JISX3010:2003』p.122「6.10.9 プラグマ演算子」

参考文献[編集]

  • 日本工業標準調査会『JISX3010 プログラム言語C』2003年12月20日改正