ソースプログラムをコンパイルする前に、ソースプログラムに対して行われる前処理をプリプロセス( ''preprocess'' )といいます。
このプリプロセスを行なうプログラムのことをプリプロセッサ( ''preprocessor'' )と呼びます。
多くのコンパイラがプリプロセッサの機能を兼ね備えていますが、cpp(1) として単体で呼び出すことも出来る場合あります。
__TOC__
前処理指令( ''preprocessing directive'' )は、以下の条件を満たす前処理トークン( ''preprocessing tokens'' )のシーケンスで構成されます『JISX3010:2003』p.110「6.10 前処理指令」。
形式
 ''preprocessing-file'':
     ''group''opt
 ''group'':
     ''group-part''
     ''group'' ''group-part''
 ''group-part'':
     ''if-section''
     ''control-line''
     ''text-line''
     # ''non-directive''
 ''if-section'':
     ''if-group'' ''elif-groups''opt else-groupopt ''endif-line''
 ''if-group'':
     # if ''constant-expression'' ''new-line'' ''group''opt
     # ifdef ''identifier'' ''new-line'' ''group''opt
     # ifndef ''identifier'' ''new-line'' ''group''opt
 ''elif-groups'':
     ''elif-group''
     ''elif-groups'' ''elif-group''
 ''elif-group'':
     # elif ''constant-expression'' ''new-line'' ''group''opt
 else-group:
     # else ''new-line'' ''group''opt
 endif-line:
     # endif ''new-line''
 control-line:
     # include ''pp-tokens'' ''new-line''
     # define ''identifier'' ''replacement-list'' ''new-line''
     # define ''identifier'' ''lparen'' ''identifier-list''opt)
     ''replacement-list'' ''new-line''
     # define ''identifier'' ''lparen'' ... ) ''replacement-list'' ''new-line''
     # define ''identifier'' ''lparen'' ''identifier-list'' , ... )
     ''replacement-list'' ''new-line''
     # undef ''identifier'' ''new-line''
     # line ''pp-tokens'' ''new-line''
     # error ''pp-tokens''opt''new-line''
     # pragma ''pp-tokens''opt''new-line''
     # ''new-line''
 text-line:
     ''pp-tokens''opt''new-line''
 non-directive:
     ''pp-tokens'' ''new-line''
 ''lparen'':
     a ( character not immediately preceded by white space
 ''replacement-list'':
     ''pp-tokens''opt
 ''pp-tokens'':
     ''preprocessing-token''
     ''pp-tokens'' ''preprocessing-token''
 ''new-line'':
     the ''new-line'' ''character''
 ''identifier-list'':
     ''identifier''
     ''identifier-list'' , ''identifier''
制約事項
最初のトークンは # 前処理トークンで、（翻訳フェーズ4の開始時に）ソースファイルの最初の文字（オプションで改行文字を含まない空白の後）か、少なくとも1つの改行文字を含む空白の後に続く # 前処理トークンです。
シーケンスの最後のトークンは、シーケンスの最初のトークンに続く最初の改行文字です。
関数型マクロの呼び出しの中にあっても、改行文字で前処理指令を終了します。
テキスト行は、 # 前処理トークンで始めてはならない。非ディレクティブは、シンタックスに表示されるディレクティブ名のいずれかで始まってはならない。また、非ディレクティブは、構文に現れたディレクティブ名で始まってはならない。
スキップされるグループ(6.10.1)の中にある場合、ディレクティブの構文は、ディレクティブ名と次の改行文字の間に、任意の前処理トークンのシーケンスができるように緩和されます。
前処理指令により、ソースファイルのセクションを条件付きで処理・スキップしたり、他のソースファイルをインクルードしたり、マクロを置き換えたりすることができます。
ファイルをインクルードしたり、マクロを置き換えたりすることができます。
概念的には翻訳ユニットの翻訳前に行われるため、これらの機能は前処理と呼ばれます。
前処理ディレクティブ内の前処理トークンは、特に断りがない限り、マクロ展開の対象にはなりません。
条件付き取り込み( ''Conditional inclusion'' )を制御する式は、以下を除き、整数の定数式でなければなりません『JISX3010:2003』p.112「6.10.1 条件付き取り込み」
。
識別子（キーワードと同一のものを含む）は以下のように解釈されます。
形式:
     defined ''identifier''
または
     defined ( ''identifier'' )
識別子がマクロ名として現在定義されている場合 (つまり、定義済みであるか、同じ識別子を持つ #undef 指令を介さずに #define 前処理指令の対象となっている場合)、1 に評価され、そうでない場合は 0 に評価されます。
条件付包含式( conditional inclusion expression )は、以下の形式の単項演算子式を含むことができます。
     __has_c_attribute ( ''pp-tokens'' )
この演算子式は、pp-tokens を属性トークンとして解釈して指定された名前の属性を実装がサポートしている場合は、整数定数の形式に一致する0でないpp-numberで置き換えられ、そうでない場合は0で置き換えられます。pp-tokensは，属性トークンの形式に一致しなければならない。
すべてのマクロ置換が行われた後に（制御式となる前処理トークンのリストに）残る各前処理トークンは、トークンの語彙形式でなければならない（§ 6.4）。
セマンティクス
また、#ifdef、#ifndef、および定義済み条件付き包含演算子は、 __has_c_attribute を定義済みマクロの名前であるかのように扱うものとします。識別子 __has_c_attribute は、本節で言及されていないいかなる文脈にも登場してはなりません。
     # if ''identifier'' ''new-line'' ''group''opt
     # elif ''identifier'' ''new-line'' ''group''opt
という形式の前処理指令は、制御定数式がゼロ以外で評価されるかどうかをチェックします。
評価の前に、制御する定数式となる前処理トークンのリスト内のマクロ呼び出しは、通常のテキストと同様に置き換えられます（定義された単項演算子によって変更されたマクロ名を除く）。この置換処理の結果として定義されたトークンが生成されたり、定義された単項演算子の使用がマクロ置換前の 2 つの指定された形式のうちの 1 つと一致しない場合、その動作は未定義です。マクロの展開とdefinedおよび__has_c_attribute単項演算子の評価によるすべての置き換えが行われた後、残りのすべての識別子(キーワードと語彙的に同一のものを含む)がpp-number 0に置き換えられこの未定義の識別子を 0 と解釈する挙動のため、識別子のミススペルがあたかも 0 を定義しているように見える事による発見困難なバグの原因になります。⇒ #未定義マクロの値、その後、各前処理トークンがトークンに変換されます。結果のトークンは、6.6の規則に従って評価される制御定数式を構成します。このトークンの変換と評価のために，すべての符号付き整数型とすべての符号なし整数型は，それぞれヘッダーで定義されているintmax_t型とuintmax_t型と同じ表現を持っているかのように振る舞います。これには、文字定数の解釈も含まれており、エスケープシーケンスを実行文字セットのメンバーに変換する必要がある場合もあります。これらの文字定数の数値が、同一の文字定数が式の中（#ifや#elif指令の中を除く）に現れたときに得られる値と一致するかどうかは、実装で定義されます。また、1文字の文字定数が負の値を持つことができるかどうかは、実装で定義される。
     # ifdef ''constant-expression'' ''new-line'' ''group''opt
     # ifndef ''constant-expression'' ''new-line'' ''group''opt
は、識別子がマクロ名として現在定義されているかどうかをチェックする。これらの条件は、それぞれ #if defined identifier および #if !defined identifier と同じです。
各ディレクティブの条件は、順にチェックされます。入れ子になっている条件式のレベルを把握するために、指令は指令を決定する名前のみで処理され、残りの指令の前処理トークンは無視され、グループ内の他の前処理トークンも無視されます。制御条件が真（0以外）と評価された最初のグループだけが処理され、それ以降のグループはスキップされ、その制御指令はスキップされたグループに含まれているかのように処理されます。どの条件も真と評価されず、#else ディレクティブがある場合は、#else で制御されるグループが処理されます。#else ディレクティブがない場合は、#endif までのすべてのグループがスキップされます。
/* Fallback for compilers not yet implementing this feature. */
__has_c_attribute */
/* Standard attribute is available, use it. */
/* Vendor attribute is available, use it. */
/* Fallback implementation. */
前方参照：マクロの置換（6.10.3）、ソースファイルのインクルード（6.10.2）、最大の整数型
マクロ展開と定義済み単項演算子による置換がすべて実行された後、残りのすべての識別子(キーワードと語彙的に同一のものを含む)は pp-number§6.4.8 ''Preprocessing numbers'' 0 に置換されます。したがって、未定義マクロの値は 0 となります。
ソースファイル取り込み( Source file inclusion )とは、他のソースファイルを取り込む前処理指令です『JISX3010:2003』p.113「6.10.2 ソースファイル取り込み」。
制約
include ディレクティブは、インプリメンテーションによって処理可能なヘッダーまたはソースファイルを特定するものです。
セマンティクス
     # include < ''h-char-sequence'' > ''new-line''
この形式は、実装で定義されている一連の場所を検索して、 < と > の間の指定された順序で一意に識別されるヘッダーを探し、 そのディレクティブをヘッダーの内容全体で置き換える。どのように場所を指定するか、どのようにヘッダーを特定するかは、実装で定義されます。
     # include " ''q-char-sequence'' " ''new-line''
は、" デリミタ "の間の指定された順序で特定されるソースファイルの内容全体で、その指示を置き換えます。指定されたソースファイルは、実装で定義された方法で検索されます。この検索がサポートされていない場合や、検索に失敗した場合は、次のように書かれているかのように、指令が再処理されます。
     # include < ''h-char-sequence'' > ''new-line''
は、元のディレクティブと同じ内容のシーケンス（もしあれば > 文字を含む）で置き換えられます標準ヘッダー（stdio.hなど）を、#include "ヘッダー名"の形式で取り込む事は、重大なセキュリティホールとなり得るので、厳に慎むべきです。。
     # include ''pp-tokens'' ''new-line'' 
(前の2つの形式のいずれとも一致しないもの)が許可されます。ディレクティブの行の後の前処理トークンは、通常のテキストと同様に処理されます（現在マクロ名として定義されている各識別子は、その前処理トークンの置換リストで置換されます）。すべての置き換えの後に生じる指令は、前の2つの形式のうちの1つと一致しなければならず、その後、適切に処理されます。
    #define INCFILE "vers1.h"
    #define INCFILE "vers2.h" // and so on
    #define INCFILE "versN.h"
前方参照：マクロの置き換え(6.10.3)
インクルードガードとは、複数回ヘッダーをインクルードすることを防止する方法です。
インクルードガードの記述は次のようになっている。
/*
ソースコード
このヘッダーをインクルードする際、
2回目以降は「インクルードガード用の識別子」が定義されているため、
ソースコードが読み飛ばされる。
また、コンパイラによっては次のようなインクルードガードの記述もある。
マクロ置き換えとは、マクロ名を置換要素並びで置き換える前処理指令です『JISX3010:2003』p.114「6.10.3 マクロ置き換え」。
マクロ置き換えには、オブジェクト形式マクロと関数形式マクロがある。
次の形式の前処理指令をオブジェクト形式マクロと呼ぶ。
形式
     # define ''identifier'' ''replacement-list'' ''new-line''
defineの直後に記述してある識別子をマクロ名( ''object-like macro'' )という。
オブジェクト形式マクロ( ''object-like macro'' )は、
これ以降のマクロ名を置換要素並びで置き換える。
次の形式の前処理指令を関数形式マクロ( function-like macro  )と呼ぶ。
形式
     # define ''identifier'' ''lparen'' ''identifier-listopt'' ) ''replacement-list'' ''new-line''
     # define ''identifier'' ''lparen'' ... ) ''replacement-list'' ''new-line''
     # define ''identifier'' ''lparen'' ''identifier-list'' , ... ) ''replacement-list'' ''new-line''
識別子並びは省略可能で、仮引数を指定し、個々の仮引数は「,」で区切り、最後に省略記号「...」を指定してもよい。
関数形式マクロは、
これ以降のマクロ名とそれに続く「(」からそれに対応する「)」までを、
置換要素並びで置き換える（マクロ呼出し）。
実引数置換とは、関数形式マクロの実引数を識別した後行われ、実引数の中に含まれるすべてのマクロの展開後、置換要素並びの中の仮引数を対応する実引数で置き換える『JISX3010:2003』p.116「6.10.3.1 実引数置換」。
ただし、#前処理字句または##前処理字句が前にある仮引数と、##前処理字句が後に続く仮引数とは除く。
実引数置換の前に、実引数の前処理字句列を完全にマクロ置き換えする。
このとき、実引数ごとに、その前処理字句列が、現在の前処理ファイルの残りとなっているものとみなして処理する。
つまり、他の前処理字句列が存在しないかのように扱われる。
置換要素並びの中に現れる識別子__VA_ARGS__は、それが一つの仮引数であるかのように扱われなければならない。
それを、可変個数の実引数が形成する前処理字句列で置き換える。
演算子は、文字列化演算子と呼ばれ、関数形式マクロの置換要素並びの中の仮引数の直前に付けることができ、#前処理字句と仮引数を、実引数を「"」で囲んだ単純文字列リテラルで置き換える『JISX3010:2003』p.116「6.10.3.2 #演算子」。
その際、「"」文字及び「\」文字の前に、「\」文字が挿入される。
演算子は、トークン連結演算子と呼ばれ、オブジェクト形式マクロ又は関数形式マクロの置換要素並びの中で使われ、その直前にある字句とその直後にある字句を連結する『JISX3010:2003』p.116「6.10.3.3 ##演算子」。
関数形式マクロの置換要素並びの中で、仮引数の直前又は直後に##前処理字句がある場合、その仮引数を対応する実引数で置き換える。
実引数置換、#演算子及び##演算子の処理、プレースマーカー前処理字句の削除を行った後、さらに置き換えるべきマクロ名があるかどうかを調べるために、ソースファイル上のその後のすべての前処理字句とともにその結果の前処理字句を再走査する『JISX3010:2003』p.117「6.10.3.4 再走査と再置き換え」。
現在置き換え中のマクロの名前を置換要素並びのこの走査中に検出した場合や入れ子になった置き換えで現在置き換え中のマクロの名前を検出した場合は、置き換えは行わない。
完全にマクロ置き換えした結果の前処理字句列は、前処理指令の形をしていたとしても、前処理指令として処理することはない。
マクロ定義の有効範囲は、そのマクロ定義から、対応する#undef指令を検出するまで又は前処理翻訳単位の最後までです『JISX3010:2003』p.117「6.10.3.5 マクロ定義の有効範囲」。
マクロ定義を無効にするには、次のように記述する。
識別子で無効にしたいマクロ名を指定する。
行制御とは、この指令の次のソース行の行番号を指定する前処理指令です『JISX3010:2003』p.120「6.10.4 行制御」。
行制御の記述は、次の2通りがある。
この指令の次のソース行の行番号が、数字列で指定された行番号となる。
以下の10進整数でなければならない。
この指令は、同様に行番号を設定し、s文字列でソースファイル名を置き換える。
また、次の形式の行制御の前処理指令もある。
この形式の前処理字句列は、
マクロ置き換えで、上の2通りの形式のいずれかと一致せねばならず、
一致した形式の規則に従って処理する。
エラー指令とは、処理系に対し、指定された前処理字句の列を含む診断メッセージを出力する前処理指令です『JISX3010:2003』p.121「6.10.5 エラー指令」。
エラー指令の記述は次のようになっている。
前処理字句列で出力する診断メッセージを指示する。
プラグマ指令( ''Pragma directive'' )は、以下のような形式をしています。
形式
 # pragma ''pp-tokens''opt ''new-line''
pragma の直後の前処理トークン( ''preprocessing toke'' )が STDC が ではない場合（マクロ置換の前）は、実装が定義した方法で動作します。
このような動作は、翻訳の失敗や、翻訳者や結果としてのプログラムの動作を不適合にする可能性があります。
実装で認識されていないプラグマは無視されます。
前処理トークンである STDC が pragma の直後にある場合（マクロ置換の前）は、指令のマクロ置換は行われず、指令は次のような形になります。
意味については別述。
 ''standard-pragma'':
     # pragma STDC FP_CONTRACT ''on-off-switc''h
     # pragma STDC FENV_ACCESS ''on-off-switch''
     # pragma STDC FENV_DEC_ROUND ''dec-direction''
     # pragma STDC FENV_ROUND ''direction''
     # pragma STDC CX_LIMITED_RANGE ''on-off-switch''
 ''on-off-switch'': one of
     ON OFF DEFAULT
 ''direction'': one of
     FE_DOWNWARD FE_TONEAREST FE_TONEARESTFROMZERO FE_TOWARDZERO FE_UPWARD FE_DYNAMIC
 ''dec-direction'': one of
     FE_DEC_DOWNWARD FE_DEC_TONEAREST FE_DEC_TONEARESTFROMZERO FE_DEC_TOWARDZERO FE_DEC_UPWARD FE_DEC_DYNAMIC
空指令とは、何の効果もない前処理指令です。
空指令の記述は次のようになっている。
『JISX3010:2003』p.121「6.10.7 空指令」
事前定義済みマクロ定数がある[http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1653.htm N1653 Working draft changes for C99 preprocessor synchronization][http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#630 CWG Issue 630. Equality of narrow and wide character values in the basic character set]。
以下のサブクラスに記載されている定義済みマクロの値は、翻訳単位全体で一定です（ただし、__FILE__ と __LINE__ を除く__LINE__ はともかく、__FILE__ は翻訳単位全体に一定では？との当然の疑問がありますが、#line でファイル名を指令された場合に変わりえます。）。
これらのマクロ名および定義された識別子は、#define または #undef 前処理指令の対象になってはなりません。その他の定義済みマクロ名は、先頭にアンダースコアを置き、その後に大文字または2つ目のアンダースコアを置きます。
実装では、マクロ __cplusplus を事前に定義してはならず、また、標準のヘッダーでも定義してはならない。
以下のマクロ名は、実装によって定義されるものとする。
__DATE__ 
ここで、月の名前はasctime関数で生成されたものと同じであり、ddの最初の文字は、その値が10未満の場合はスペース文字です。
翻訳の日付が得られない場合は、実装で定義された有効な日付を供給しなければならない。
__FILE__
__LINE__
__STDC__
適合する実装であることを示すための整数定数 1。
__STDC_HOSTED__
__STDC_VERSION__
__TIME__
asctime関数で生成された時間と同様に、"hh:mm:ss" 形式の文字列リテラル。
翻訳時刻が得られない場合は、実装で定義された有効な時刻を提供する。
[https://paiza.io/projects/sbv6TqBKcvPnwKPlE7HpmA?language=c コード例]
int main(void){
    printf("__DATE__ = %s\n", __DATE__);
    printf("defined(__DATE__) == 0");
    printf("__FILE__ = %s\n", __FILE__);
    printf("defined(__FILE__) == 0");
    printf("__LINE__ = %d\n", __LINE__);
    printf("defined(__LINE__) == 0");
    printf("__STDC__ = %d\n", __STDC__);
    printf("defined(__STDC__) == 0");
    printf("__STDC_HOSTED__ = %d\n", __STDC_HOSTED__);
    printf("defined(__STDC_HOSTED__) == 0");
    printf("__STDC_VERSION__ = %ld\n", __STDC_VERSION__);
    printf("defined(__STDC_VERSION__) == 0");
    printf("__TIME__ = %s\n", __TIME__);
    printf("defined(__TIME__) == 0");
}
結果（paiza.io /language=c）
__DATE__ = Aug  6 2021
__FILE__ = Main.c
__LINE__ = 15
__STDC__ = 1
__STDC_HOSTED__ = 1
__STDC_VERSION__ = 201710
__TIME__ = 23:45:08
結果（Visual Studio 2017 /std:c17 /Windows 10）
__DATE__ = Aug  7 2021
__FILE__ = C:\Users\user1\Source\Repos\macros\macros.c
__LINE__ = 14
defined(__STDC__) == 0
__STDC_HOSTED__ = 1
__STDC_VERSION__ = 201710
__TIME__ = 08:54:00
結果（clang11/FreeBSD13）
__DATE__ = Aug  7 2021
__FILE__ = macros.c
__LINE__ = 14
__STDC__ = 1
__STDC_HOSTED__ = 1
__STDC_VERSION__ = 201710
__TIME__ = 08:59:18
以下のマクロ名は、実装によって条件付きで定義されています。
__STDC_ISO_10646__
__STDC_MB_MIGHT_NEQ_WC__
__STDC_UTF_16__
__STDC_UTF_32__
以下のマクロ名は、実装によって条件付きで定義されます。
__STDC_ANALYZABLE__
__STDC_IEC_559__
__STDC_IEC_559_COMPLEX__
__STDC_LIB_EXT1__
__STDC_NO_ATOMICS__
__STDC_NO_COMPLEX__
__STDC_NO_THREADS__
__STDC_NO_VLA__
__STDC_NO_COMPLEX__ を定義する実装は、 __STDC_IEC_559_COMPLEX__ を定義してはならない。
※ __DATE__と__TIME__はそれを使わなければ絶対に解決できない課題を解決するためにのみ使うべきであり、それ以外の場合に安易に使うべきではない。特に、バージョン値の代用としての使用は不適切です。
『JISX3010:2003』p.121「6.10.8 あらかじめ定義されたマクロ名」
プラグマ演算子は、指定した文字列リテラルを文字列解除し、その結果をプラグマ指令の中の前処理字句列として実行する『JISX3010:2003』p.122「6.10.9 プラグマ演算子」。
つまり、次の形式の指令は、その次の形式でも表現できる。
_Pragma("listing on \"..\\listing.dir\"")