C 言語のプリプロセッサはマクロプロセッサであり、C コンパイラが実際のコンパイルの前にプログラムの変換を行うさいに自動的に使用される。マクロプロセッサというのは、マクロを定義できることから来ている。マクロは、ある構文に対する短縮形である。
C プリプロセッサには 4 つの独立した機能があり、目的に応じた使い方ができる。
C 言語のプリプロセッサは、実装によって細部が異なることがある。本マニュアルでは、C 互換コンパイラプロセッサ(C Compatible Compiler Preprocessor)である、GNU C プリプロセッサについて説明する。GNU C プリプロセッサは、ANSI C 言語規格で規定されている機能と上位互換性を持つ。
ANSI C 言語規格では、今日、多くの C で書かれたプログラムで良く使われる多くの無害な構文を拒絶することを要求している。この非互換性はユーザに取っては不便なので、GNU C プリプロセッサは、これらの構文を受け付けるようになっている。このため、ANSI C 言語規格に則るためには、厳密には、-trigraphs、-undef、-pedantic の各オプションを指定しなければならないということになる。
C プリプロセッサは C 言語系の言語向けに設計されている。他の種類の言語に対して使うと問題が発生する可能性がある。それは、その出力が C 言語で扱われることを想定しているからである。例えば、Cプリプロセッサは空白を余分に出力して、 C のトークンの接合が思いがけずに起こるのを避けることがある。これが、他の言語では問題になることもあるだろう。
C プリプロセッサの機能のほとんどは、特定の制御子を使うことにより明示的に要求しない限りは、動作しない。(プリプロセッサ制御子は、# で始まる行である。see Directives).
しかし、制御子が無くても、全ての入力行に対して常に行われる処理が三つある。
最初の二つの変換は、他のほとんど全ての構文解析が行われる前、そしてプリプロセッサ制御子が認識される前に行われる。このため、例えば、どこででもはバックスラッシュと改行で見た目の行を分けることが出来る。(トリグラフが使われている場合は除く。以下を参照のこと。)
/* */ # /* */ defi\ ne FO\ O 10\ 20
この例は、#define FOO 1020 と等価である。エスケープシーケンスでさえもバックスラッシュと改行で分けることが出来る。例えば、"foo\bar" の\ と b の間で分けると次のようになる。
"foo\\ bar"
この動作は曖昧である。他の全ての文脈では、二つのバックスラッシュを続けて書くことで、バックスラッシュを通常の文字として文字列定数に含めることが出来る。上の場合は例外となる。しかし、これが ANSI C 規格が要求している動作である。(厳密な ANSI C では、文字列定数の中に改行を置くことを許していないので、この問題については考慮していないのである。)
以上の三つの全ての変換に対して、幾つか例外がある。
#include 制御子の中、つまり、< と > で囲まれたファイル名を置く場所では、C のコメントと定義済マクロ名は認識されない。
この例外が関係するのは、-trigraphs オプションを指定して、トリグラフ処理を有効にしたときだけである。
プリプロセッサの機能のほとんどは、プリプロセッサ制御子を指定して明示的に要求したときにのみ有効になる。
プリプロセッサ制御子は、# で始まる行のことである。# の後ろには、制御子名 である識別子が続く。例えば、#define は、マクロを定義する制御子である。空白を、# の前後に置いても良い。
有効な制御子名は固定されていて、プログラム中で新しい制御子を定義することはできない。
制御子には引数を必要とするものがある。引数は、制御行の残りの部分を構成し、制御子名と空白により区切られている必要がある。例えば、#define は、次にマクロ名とマクロの展開形を来なければならない。See Simple Macros 参照。
前処理制御子は、通常は二行以上にまたがることはできない。見かけ上は、バックスラッシュと改行により行を分けることが出来るが、意味的にはなんら違いはない。改行を含むコメントも制御行を複数行に分けることが出来るが、コメントは制御行が解釈される前に空白に変換されてしまう。制御行で改行が意味を持つのは、文字列定数または文字定数の中だけである。しかし、プリプロセッサの出力を受け取ることになる C コンパイラ本体は、ほとんどが、改行を含む文字列定数や文字定数を受け付けないことに注意して欲しい。
マクロ展開の結果が # と制御子名になるようにすることは出来ない。例えば、foo を define に展開されるマクロとして定義した場合、#foo は有効な前処理制御子ではない。
ヘッダファイルは、C の宣言とマクロ定義(see Macros)が含まれ、多数のソースで共有されるべきものである。プログラムの中でヘッダファイルを使うには、C 前処理指示子 #include を使う。
#include の書き方#include は何をするかヘッダファイルの役割には二通りある。
ヘッダファイルをインクルードすることは、C のコンパイルにおいては、ヘッダファイルを、それを必要とする各ソースファイルの中にコピーすることと同じ結果になる。だが、そういうコピーは時間の無駄だし、エラーを起こしやすい。ヘッダファイルを使うと、関係する宣言は一箇所にしか現れない。変更が必要な場合は、一箇所を変えれば良く、そのヘッダファイルをインクルードしているプログラムは、次のコンパイル時には自動的に新しいバージョンを使うことになる。ヘッダファイルを使うことにより、全てのコピーを見つけ出して変更するという手間をなくせるだけでなく、コピーを一つ見のがしたために、プログラムに不整合が起きるという危険もなくすことができる。
ヘッダファイルには習慣として .h で終わる名前を付ける。ヘッダファイル名の中に普通でない文字を使うと、移植性を損なうことになるので、避けよう。
#include Directiveユーザのヘッダファイルもシステムのヘッダファイルも、#include を使ってインクルードされる。これには三種類の書き方がある。
#include <file>
-I を使う(see Invocation)。-nostdinc オプションを指定すると、システムの標準ディレクトリの検索を行なわない。この場合、読者が指定したディレクトリだけを検索する。
この形の #include を構文解析するのは、ちょっと特別である。というのは、<...> の中ではコメントは認識されないからである。つまり、#include <x/*y> と書いた場合、この中の /* はコメントの開始とならず、この制御子は、x/*y という名前のシステムのヘッダファイルをインクルードすることを指示する。もちろん、こんな名前のヘッダファイルが Unix 上に存在することはあまりないだろう。こういうファイルがあると、シェルのワイルドカードの機能のためにファイルの操作が面倒になるからである。
引数 file は、> という文字は含んではいけない。しかし、< は含んでも良い。
#include "file"
-I- オプションを指定すると、カレントディレクトリを特別扱いするのを抑止する。
引数 file は " という文字を含んではいけない。file の中にバックスラッシュがあると、エスケープ文字ではなく、普通のテキスト文字として扱われる。C の文字列定数では適切な文字エスケープ列であっても、プリプロセッサではどれも処理されない。つまり、#include "x\n\\y" とすると、バックスラッシュを三つ含むファイル名を指定することになる。こういう振るまいが何故役に立つのか、はっきりしないが、ANSI 規格ではそう規定している。
#include anything else
#include 制御子はなんであれ、計算形 include である。anything else というテキストの中にマクロ呼び出しがあるかどうかが検査される。あればそれは展開される(see Macros)。それが終わったとき、その結果は上記の二つの形式の一つに一致しなければならない。特に、展開されたテキストは最終的には、ダブルクォートか各括弧で囲まれていなければならない。
この機能により、プログラムの後の点で使われるファイル名を制御するマクロを定義することができる。この機能の一つの応用としては、使用すべきシステムのインクルードファイル名を指定することで、プログラムでサイト固有のコンフィギュレーションファイルを使うことができる。こうすると、プログラムを色々な種類のオペレーティングシステムに移植する際に、必要なシステムのヘッダファイルが色々な場所にある場合に役に立つ。
#include Works#include 制御子は、現在のファイルの残りを続けて処理する前に、指定されたファイルを入力として走査することを C プリプロセッサに指定する働きをする。プリプロセッサの出力は、既に生成済みの出力の後ろに、インクルードファイルから生じる出力が続き、その後に #include 制御子の後のテキストから来る出力が続く。例えば、ヘッダファイル header.h
が以下のように与えられたとする。
char *test ();
また、メインプログラム program.c はこのヘッダファイルを以下のように使っているものとする。
int x;
#include "header.h"
main ()
{
printf (test ());
}
入力を program.c とした場合に C プリプロセッサの出力は以下のようになる。
int x;
char *test ();
main ()
{
printf (test ());
}
インクルードされるファイルは、宣言やマクロ定義に限定されない。それは単に典型的な使い方というだけである。C プログラムの任意の断片を別のファイルからインクルードすることができるのである。インクルードする方のファイルに、インクルードされるファイルで完結する文の始まりがあっても良い。あるいはインクルードされるファイルの中で始まり、インクルードされる側で終わっている文があっても良い。ただし、コメントや文字列定数、文字手椅子はインクルードされるファイルで始まって、インクルードしている方のファイルで終わってはならない。インクルードされたファイルの中に、終りのないコメントや文字列定数、文字定数があると、それらはインクルードされたファイルの最後で終わると考えられる。この時、エラーメッセージが出る。
ヘッダファイルで、関数定義のような、ある文法的な単位を開始したり、終了したりすることも出来るが、混乱するだけなのでやらない方が良い。
#include 制御子の行は、インクルードされたファイルに最後の改行がなくても、C プリプロセッサは常に一個の独立した行として扱う。
あるヘッダファイルが別のヘッダファイルをインクルードするのは、非常に良くあることである。このため、あるヘッダファイルが二回以上インクルードされることも良く起きる。二回以上インクルードされると、ヘッダファイルに構造体型や typedef の定義があればエラーとなり、また無駄でもある。あるヘッダファイルを複数回インクルードするのを防ぎたいと常々考えるわけである。
複数回インクルードするのを防ぐ標準的な方法は、ファイルの中身全体を条件文で、以下のように囲むことである。
#ifndef FILE_FOO_SEEN #define FILE_FOO_SEEN ファイルの中身全体 #endif /* FILE_FOO_SEEN */
マクロ FILE_FOO_SEEN でそのファイルが既に一回インクルードされていることを表す。ユーザのヘッダファイルでは、このマクロ名は __ で始まってはならない。システムのヘッダファイルでは、このマクロ名はユーザプログラムとの衝突を回避するために __ で始まっていなければならない。どちらの種類のヘッダファイルでも、マクロ名はファイル名と追加のテキストを含むようにして、他のヘッダファイルとの衝突を回避するようにしなければならない。
GNU C プリプロセッサは、ヘッダファイルでこの特別な構文が使われているのを認識すると効率良く扱うようにプログラムされている。あるヘッダファイルが、#ifndef 条件文に完全に含まれているなら、そのことを記録しておく。後続の #include が同じファイルを指定しており、かつ #ifndef に現れるマクロが既に定義されているなら、そのファイルは全部スキップされ、読み出すことも行なわれない。
また、あるファイルを二回以上インクルードする必要が無いことをプリプロセッサに指示する明示的な制御子もある。これは、#pragma once であり、ヘッダファイルの内容を#ifndef 条件文で囲んだうえでで使われた。#pragma once は今では廃れた機能なので、使うべきではない。
Objective C 言語の場合には、#import という #include の変種があって、ファイルをインクルードするが、多くても一回だけしかインクルードしない。#import を #include の代わりに に使う場合は、ヘッダファイルの中身が複数回実行されるのを防ぐための条件文をヘッダファイルに入れる必要はない。
#import は、あまり良い設計ではないのでもはや使うべきではない。この機能だと、ヘッダファイルのユーザ、つまりアプリケーション・プログラマがあるヘッダファイルが一回だけしかインクルードすべきでないことを知っている必要がある。ヘッダファイルの作者は、ユーザはそんなことを知らなくても済むように書いたほうがはるかに良い。それには、#ifndef を使えば充分である。
継承 は、一つのオブジェクトやファイルがその内容を別のオブジェクトやファイルから仮想的にコピーするときに発生する。C のヘッダファイルの場合は、継承とは一個のヘッダファイルが別の一つのヘッダファイルをインクルードし、置き換えたり、何かを追加したりすることを意味する。
継承を行なっているヘッダファイルと基底となるヘッダファイルの名前が違っている場合は、継承は単純明解である。単に継承を行なうほうのヘッダファイルに #include "base" と書けば良い。
継承を行なうファイルと同じ名前を基底ファイルに与える必要が出てくるときがある。こちらは単純明解とはいかない。
例えば、あるアプリケーションプログラムがシステムのヘッダファイルsys/signal.h を使っているとしよう。だが、ある特定のシステムの/usr/include/sys/signal.h は、アプリケーションプログラムが期待している動作をしないものとする。おそらく /usr/local/include/sys/signal.h という名前で、このヘッダファイルの「ローカル」な版を定義して、システムが提供している版を上書きしたり、追加できると便利だろう。
そうするには、オプション -I. を付けてコンパイルするようにし、アプリケーションプログラムが期待しているとおりの動作するsys/signal.h を書けば良い。だが、このファイルにシステム標準の sys/signal.h をインクルードさせるのは簡単ではない。自分で書いたほうのファイルに #include <sys/signal.h>
と書いてもだめである。システム標準の方ではなくて、自分で書いた方をインクルードするからである。自分自身をインクルードすることになるので、無限に再帰する事になり、コンパイル時に致命的エラーで終わるだろう。
#include </usr/include/sys/signal.h> と書けば正しいファイルを見つけるが、これはきれいな書き方ではない。なぜなら、システムヘッダファイルが置かれている位置を仮定しているからである。これは保守を考えるとまずいやり方である。システムヘッダファイルが置かれている位置が少しでも変わったら、どこか別のところでも変更が必要になるからである。
この問題をきれいに解決するには #include_next を使う。include_next は、この名前と同じ名前の「次の」ファイルをインクルードするという意味である。この制御子は、指定されたファイルの検索を除いて #include と同じ働きをする。ヘッダファイル検索ディレクトリのリストを、現在のファイルが見つかったディレクトリの「次」から検索を始める。
-I /usr/local/inlcude を指定し、検索ディレクトリのリストには/usr/include も含まれているとしよう。どちらのディレクトリにもsys/signal.h というファイルがあるとする。普通の #include <sys/signal.h> は /usr/local/include の下のファイルを見つける。そのファイルに #include_next
<sys/signal.h> があると、そのディレクトリの次から検索を開始し、/usr/include にあるファイルを見つける。
マクロは一種の省略形であり、一度定義するとそれ以降で使うことができる。C プリプロセッサのマクロにはたくさんの入り組んだ機能がある。
単純マクロは一種の省略形である。それは、あるコードの断片を表す名前である。定数の宣言と呼ぶ人もいる。
あるマクロを使うためには、その前に #define 制御子で明示的に定義(define)しなければならない。#define に続いて、マクロ名を置き、そのつぎにそれを省略形としたいコードを置く。例えば、
#define BUFFER_SIZE 1020
という定義は、BUFFER_SIZE という名前のマクロをその次に書かれた1020 の省略形として定義する。この #define の制御子以降のどこかで、次の形の C の文があったなら、
foo = (char *) xmalloc (BUFFER_SIZE);
C プリプロセッサはマクロ BUFFER_SIZE を認識し、展開するので以下のようになる。
foo = (char *) xmalloc (1020);
マクロ名を全部大文字にするのは標準的な習慣である。一目見てどの名前がマクロかわかるとプログラムが読みやすくなるのである。
通常、マクロ定義は一行に収まらなければならない。これは、全ての C の前処理制御子と同様である。(長いマクロ定義を見かけ上、バックスラッシュ-改行を使って複数行に分けることは可能である。) 例外が一つある。改行が文字列定数または文字定数の中にあれば、マクロ定義に含めることが可能である。これは、マクロ定義に対応の取れていない引用符を含めることが不可能であることによる。マクロ定義は、文字列や文字定数の終りとなる引用符の対応が取れるところまで自動的に延長される。マクロ定義中のコメントには改行が入っていても良い。コメントはその中身が何であろうと全部空白に変えられるので、何も違いがないからである。
上記を除けば、マクロ本体に何が置けるかについての制限はない。括弧は釣り合っている必要はないし、本体が正しい C のコードらしくなくても良い。(だが、正しい C のコードでない場合は、そのマクロを使ったときにはC コンパイラがエラーメッセージを出すであろう。)
C プリプロセッサはプログラムを逐次的に走査するので、マクロ定義はそれを書いた場所から有効になる。このため、C プリプロセッサに対し以下のような入力を与えると、
foo = X; #define X 4 bar = X;
次のような出力が得られる。
foo = X; bar = 4;
プリプロセッサがマクロ名を展開した後、マクロ定義の本体が残りの入力の前に追加され、マクロ呼び出しの検査が引き続き行なわれる。このため、マクロ定義本体には他のマクロの呼び出しを含めることができる。例えば、
#define BUFSIZE 1020 #define TABLESIZE BUFSIZE
とした後、TABLESIZE という名前がプログラム中で使われると、二段階の展開を受け、最終的には 1020 となる。
これは、TABLESIZE を 1020 と定義するのと全く同じではない。TABLESIZE の #define 自体は、指定した本体をそのまま使う。この場合は、BUFSIZE になり、それがまたあるマクロ名になっているかどうかの検査は行なわない。TABLESIZE を使った時にだけ、展開結果にさらにマクロ名が含まれているかどうかの検査が行なわれる。
単純マクロは、いつ使われても、常に全く同じテキストを表す。マクロは、引数を受け付けるときにより柔軟性を発揮する。引数とは、コードの断片であり、マクロが使われる度に与えるものである。この断片は、マクロ定義の指示に従って、マクロの展開形に取り込まれる。引数を受け付けるマクロは 関数型マクロと呼ばれる。使い方が関数呼び出しに似ているからである。
引数を使うマクロを定義するには、#define 制御子を、マクロ名の後に 引数名のリストを括弧で囲んで書くようにする。引数名は任意の有効な C の識別子であり、カンマと、それにオプションで空白を付けて、区切る。開き括弧はマクロ名の直後に、空白を入れずに置かなければならない。
例えば、次のマクロは、二つの数値の最小値を計算する。これは多くの C プログラムで定義されている。
#define min(X, Y) ((X) < (Y) ? (X) : (Y))
(これは、GNU C では「最小値」マクロを定義する最善の方法ではない。詳細については See Side Effects)
引数があることを想定しているマクロを使うには、マクロ名の後ろに、括弧で囲んだ 実引数のリストをカンマで区切って書く。指定した実引数の数は、マクロが想定している引数の数と一致しなければならない。マクロ min の使い方の例は、min (1, 2) や min (x + 28, *p) 等である。
マクロの展開テキストは、指定した引数に依存する。マクロの各引数名は、マクロ定義全体で、対応する実引数に置き換えられる。上で定義したのと同じマクロ min を使うと、min (1, 2) は以下のように展開される。
((1) < (2) ? (1) : (2))
ここで 1 は X を置き換えたものであり、2 は Y を置き換えたものである。
同様に、min (x + 28, *p) は以下のように展開される。
((x + 28) < (*p) ? (x + 28) : (*p))
実引数の中にある括弧は釣合いが取れてないといけない。括弧内にあるカンマは引数を終わらせない。しかし、大括弧と中括弧については釣合いを取る必要はなく、その中にあるカンマで引数を区切ることを妨げない。つまり、
macro (array[x = y, x + 1])
とすると、macro に二つの引数を渡す。array[x = y とx + 1] である。array[x = y, x + 1] を一個の引数として渡したければ、C のコードとしては等価な、array[(x = y, x + 1)] のように書かなければならない。
実引数がマクロ本体に代入された後、その結果全体が残りの入力の前に追加され、マクロ呼び出しの検査が続く。このため、実引数は他のマクロの呼びだしを含むことが出来る。呼び出されるマクロは引数があっても無くても良いし、同じマクロであっても構わない。マクロ本体の方も他のマクロの呼びだしを含むことが出来る。例えば、min (min (a, b), c) は以下のように展開される。
((((a) < (b) ? (a) : (b))) < (c) ? (((a) < (b) ? (a) : (b))) : (c))
(ここで複数行に分けているのは見易くするためであり、実際には行は分けられない。)
マクロ foo が引数を一つ取る場合、それに空の引数を渡したい場合は、foo ( ) のように、少なくとも空白を括弧の間に置かなければならない。foo () としたのでは引数を与えないことになるので、foo が引数を一つ想定している場合にはエラーになる。ただし、foo0 () とするのは、以下のように、引数が 0 個に定義されているマクロを呼び出す正しい方法である。
#define foo0() ...
マクロ名の後ろに開き括弧以外の何か(ただし、空白、タブやコメントは無視する)が来た場合は、それはマクロ呼出しにはならず、プリプロセッサは書いてあるものをそのままにする。このため、読者のプログラムの中で、マクロと同じ名前を変数や関数にすることもできる。これは、その名前を使う度に、マクロを参照している(実際の引数のリストが続く場合)のか、変数や関数(引数リストが後ろに続かない場合)を参照しているのかを選ぶことができる。
一つの名前をこのように二つの方法で使い分けるのは混乱の元であり、その二つが実質的に同じ意味を持つ場合以外は避けるべきだろう。その場合とは、すなわち、その名前がマクロでもあり関数でもあり、かつ、二つの効果が類似の場合である。その名前を単に関数と考えることができる。呼び出す以外の目的でその名前を使うと(例えば、アドレスを取る)、関数を参照し、一方、呼出しの場合はマクロが展開され、等価ではあるが寄り良いコードを生成することになる。例えば、関数名 min を、それと同じ名前のマクロを定義している同一のソースファイルで使うことができる。引数リストなしで &min と書けば、関数を参照する。引数リストを付けて min (x, bb) と書けば、これはマクロが展開される。(min) (a, bb) と書くと、min という名前の後に開き括弧が来ないので、マクロは展開されず、関数 min を呼び出すことになる。
同じ名前を単純マクロと引数付きマクロの両方で定義してはならない。
引数付きマクロの定義では、引数名のリストはマクロ名のすぐ後ろから、空白を入れずに置かなければならない。マクロ名の後に空白があると、マクロは引数を取らないものとして定義され、その行の残り全体が展開形となる。こうなっている理由は、引数なしのマクロで、その定義が括弧で囲んだ識別子で始まるように定義すると便利なことが多いからである。この空白に関する規則に従うと、
#define FOO(x) - 1 / (x)
とすることもできるし(これは、FOO に引数を一つ取らせ、展開形はその引数の逆数を符号反転したものとする)、
#define BAR (x) - 1 / (x)
とすることもできる(これは、BAR は引数無しとし、常に(x) - 1 / (x) と展開されるように定義する)。
引数付きのマクロを 呼び出す時は、左括弧の前に空白を置いても良いことに注意。空白があるかどうかが問題になるのは、その定義の方である。
幾つかの単純マクロが定義済である。これらは定義しなくても使用することができる。これらは二種類に分類される。標準マクロとシステム固有マクロである。
標準の定義済みマクロは、GNU C を使っている機種や OS によらずに、同じ意味で使うことができる。これらのマクロ名は、全て前後にアンダースコアが二つずつ付く。以下の表で、__GNUC__ より前にあるものは ANSI C で規格化されているものある。残りは、GNU C の拡張である。
__FILE__
#include か、入力ファイル名引数として指定されたものである。
__LINE__
__LINE__ と __FILE__ は、プログラムにより検知された不整合を報告するエラーメッセージを生成するのに便利である。そのメッセージには、不整合が見つかったソースコード行を含めることができるのである。例えば、以下のように使う。
fprintf (stderr, "Internal error: "
"negative string length "
"%d at %s, line %d.",
length, __FILE__, __LINE__);
#include 制御子は、__FILE__ と __LINE__ の展開をインクルードされたファイルに対応するように変更する。インクルードされたファイルの終りで、#include 制御子がある方の入力ファイルの処理に戻ったとき、__FILE__ と __LINE__ の展開は、#include の前の値に戻る。(ただし、__LINE__ は、制御が #include の次の行に移動するため、1 が加算される。)
__FILE__ と __LINE__ の展開は両方とも、#line 制御子が使われると変更される。See Combining Sources.
__DATE__
"Feb 1 1996" のようになる。
__TIME__
"23:59:01" のようになる。
__STDC__
ホストによっては、システムのインクルードファイルが、異なる規約、すなわち、通常は __STDC__ は 0 であるが、ユーザがC 規格に厳密に適合することを指定した場合には 1 になるという規約を使っているものもある。その場合、本プリプロセッサは、システムのインクルードファイルを処理するときはホストの規約に従い、ユーザのファイルを処理するときは GNU C の通常の規約に従う。
このマクロは、-traditional オプションが指定された場合は定義されない。
__STDC_VERSION__
yyyymmL という形の long 型整数定数である。ここで、yyyy と mm は、それぞれ、規格のバージョンの年と月である。これは、本プリプロセッサが C 規格のどのバージョンに適合しているかを知らせる。__STDC__ 同様、このバージョン番号が実装全体に対して正確であるかどうかは、プリプロセッサの出力をどの C コンパイラで処理するかに依存する。
このマクロは、-traditional オプションが指定されたときには定義されない。
__GNUC__
__GNUC__ は定義されない。このマクロの値は GNU CC の主バージョン番号を特定する(今や古くなった GNU CC バージョン 1 では 1、バージョン 2 では 2 である)。
__GNUC_MINOR__
__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 6)
という式で調べることが出来る)。この例の最後の数字 3 は、GNU CC のバグ修正のレベルを表す。この値を表すマクロはない。
__GNUG__
__GNUG__ を使って、GNU C と GNU C++ を区別することができる。
__cplusplus
__cpluspuls を使って、あるヘッダが C コンパイラによってコンパイルされているのか、C++ コンパイラによってコンパイルされているのかを調べることができる。
__STRICT_ANSI__
-ansi
オプションが指定されたときだけである。このマクロの定義はヌル文字列になる。このマクロがある理由は主に、ある種の GNU のヘッダファイルで、ANSI C と互換性のない、旧来の Unix の構文を定義しないようにするためにある。
__BASE_FILE__
__INCLUDE_LEVEL__
#include 命令に出会う度に加算され、ファイルの最後に出くわす度に減算されう。コマンド行引数で指定された入力ファイルのネストのレベルは 0 である。
__VERSION__
"2.6.0" のようになる。
__OPTIMIZE__
__CHAR_UNSIGNED__
char が、ターゲット機種で符号無しの場合に限られる。これにより標準ヘッダファイル limits.h
が正しく働くようになる。読者はこのマクロを自分で参照すべきではない。代わりに limits.h で定義されている標準のマクロを参照すべきである。プリプロセッサは、このマクロを使って、8進数で書かれた大きな文字定数を符号拡張するかどうかを決定する。The #if Directive を参照のこと。
__REGISTER_PREFIX__
m68k-aout という環境ではこれはヌル文字列に展開されるが、m68k-coff の環境では % という文字列に展開される。
__USER_LABEL_PREFIX__
__REGISTER_PREFIX__ に同じような働きをするが、アセンブラコード中のユーザが生成したラベルに付加されるプレフィックスを記述する。例えば、m68k-aout の環境では _ という文字列に展開されるが、m68k-coff の環境ではヌル文字列に展開される。このマクロは、i386 の OSF/rose や m88k というターゲットで提供している-mno-underscores オプションや rs6000 の System V Release 4 というターゲットの -mcall* オプションと組み合わせて使うことはできない。C のプリプロセッサには、通常、機種によって異なる色々な事前定義マクロがある。機種によって異なるのは、それらのマクロの目的が、使用しているシステムのタイプや機種を示すためにあるからである。このマニュアルは、全てのシステムと機種用ではあるが、これらのマクロ名が何であるかを正確に伝えることはできない。代わりに、代表的なマクロの名前を列挙しておく。cpp -dM とすると事前定義マクロの値を見ることができる。Invocation を参照のこと。
幾つかの非標準事前定義マクロは、使用するオペレーティングシステムを多かれ少なかれ、より詳細に記述する。例えば、以下の通り。
unix
unix は通常全ての Unix システムで事前定義される。
BSD
BSD は、最近のバージョンの Berkeley Unix(多分 4.3 だけ)で事前定義される。その他の非標準事前定義マクロは CPU の種類を、多かれ少なかれ、より詳細に記述する。例えば、以下の通り。
vax
vax は VAX コンピュータで事前定義される。
mc68000
mc68000 は、CPU が Motorola の 68000、68010、68020 であるコンピュータのほとんどで事前定義される。
m68k
mc68k も、CPU が Motorola の 68000、68010、68020 であるコンピュータのほとんどで事前定義される。ただし、メーカによってはmc68000 を使ったり、m68k を使ったりする。中には両方の名前を事前定義するものもある。GNU C ではどうなっているかは、読者が使っているシステムに依存する。
M68020
mc68000 とm68k に加えて、M68020 が事前定義されていることが報告されている。
_AM29K
_AM29000
_AM29K と _AM29000 の両方が事前定義されている。
ns32000
ns32000 は、National Semiconductor の 32000 シリーズを使っている計算機で事前定義されている。さらに別の非標準事前定義マクロで、システムの製造者を記述するものがある。例えば、以下の通り。
sun
sun は、Sun の全てのモデルで事前定義されている。
pyr
pyr は、Pyramid の全てのモデルで事前定義されている。
sequent
sequent は、Sequent の全てのモデルで事前定義されている。これらの事前定義シンボルは標準的でないだけでなく、ANSI 規格にも反する。なぜなら、名前がアンダースコアで始まっていないからである。このため、-ansi オプションを指定すると、これらのシンボルの定義を禁ずる。
このため -ansi が使えないことが多くなる。習慣的に非標準の事前定義シンボルに依存しているプログラムがたくさんあるからである。システムヘッダファイルでそれらを検査し、想定している名前が見つからないと正しくない宣言を生成してしまうものさえある。「醜いUnix」(Uglix)計算機に提供されているヘッダファイルは、それらは単に醜いUnix であると仮定して良いはずなのだから、どんな機種で実行されるかをテストする必要はないはずだと思うかもしれない。だが、実際にはそういうテストが良く行われており、その時習慣的な名前を使っているのである。その結果、ほとんどの C プログラムが-ansi ではコンパイルできない。我々は、GNU システムではそういう問題を避けたいのである。
では、ANSI C のプログラムの中で、そのプログラムが実行される機種型をテストするにはどうすべきか?
GNU C は、このために対応するシンボル群を提供している。これらの名前は、習慣的にシンボル名の先頭と末尾に __ を付けたものになる。すなわち、__vax__ というシンボルが Vax 上で利用可能になる等々。
GNU C プリプロセッサで事前定義される非標準名のセットは、マクロ CPP_PREDEFINES で制御される(cpp 自体がコンパイルされるときにである)。このマクロは、-D オプションを空白で区切った文字列にする。例えば、Sun 3 では、我々は以下の定義を使っている。
#define CPP_PREDEFINES "-Dmc68000 -Dsun -Dunix -Dm68k"
このマクロは普通は tm.h で指定される。
文字列化(Stringification) は、あるコード断片のテキスト表現を文字列定数に変換することである。例えば、foo (z) の文字列化した結果は、"foo (z)" となる。
C プリプロセッサにおいては、文字列化はマクロ引数がマクロ定義に代入される際についでに利用可能である。マクロ定義の本体で、引数名が現れるところで、引数名の前に文字 # を付けると、定義のその場所に対応する実引数を文字列化する。同じ実引数は、定義の他の場所に、名前に # を付けずに書けば、文字列化を受けることなく代入される。
以下に文字列化を使ったマクロ定義の例を示す。
#define WARN_IF(EXP) \
do { if (EXP) \
fprintf (stderr, "Warning: " #EXP "\n"); } \
while (0)
ここで、EXP の実引数は一つは指定された通りに if 文に代入され、もう一つは文字列化されて fprintf の引数に代入される。do と while (0) を使っているのは、WARN_IF (arg); と書けるようにするための小細工である。こうすると、C プログラマの望み通り、WARN_IF が関数呼出しのように使えるのである。Swallow Semicolon 参照。
文字列化機能は、一つのマクロ引数を一つの文字列定数に変換するだけであるという制限がある。引数を他のテキストと組み合わせて全体を文字列化する方法はない。だが、上の例を見れば、ANSI C で等価な結果を得るには、隣り合わせに置いた文字列定数は一つの文字列定数として連結されるという機能を使えば良いことが判るだろう。プリプロセッサの文字列化では、EXP の実際の値を一個の独立した文字列定数にするので、結果は以下のようになる。
do { if (x == 0) \
fprintf (stderr, "Warning: " "x == 0" "\n"); } \
while (0)
だが、C コンパイラは、文字列が三つ連続していることに気付き、それらを一個に連結するので、実質的に以下のようになるのである。
do { if (x == 0) \
fprintf (stderr, "Warning: x == 0\n"); } \
while (0)
C の文字列化は、単にコード断片のまわりに二重引用符文字を置くだけではない。文字列定数や文字定数の中にある二重引用符とバックスラッシュは全てその前にバックスラッシュを追加して、中身の正しい、C の文字列定数として有効になるようにする必要がある。このため、p = "foo\n"; を文字列化すると、"p = \"foo\\n\";" となる。ただし、文字列定数や文字定数の中にないバックスラッシュは二重化されない。\n だけなら文字列化すると "\n" になる。
文字列化されるテキスト中の空白(コメントを含む)は厳密な規則に従って扱われる。先行する、あるいは追従する空白は全て無視される。テキスト内部の空白列は、文字列化されるとそれぞれ一個の空白に変換される。
連結とは、二つの文字列を一つにつなぐことである。マクロ展開の文脈では、連結とは二つの字句単位をより大きな一つにつなぐことである。特に、マクロの実引数をもう一つの実引数や固定のテキストと連結して、より長い名前を作ることができる。長い名前は、関数名、変数名、型名、あるいは C のキーワードになりうる。別のマクロの名前になることさえあり、この場合さらに展開される。
マクロを定義するとき、連結を行なうにはマクロ本体で特別な演算子## を使う。マクロが呼び出されると、実引数が置き換えられた後、全ての ## 演算子とこの演算子の次の空白が削除される(実引数の一部の空白も含まれる)。この結果、## の両側の文法的トークンが連結される。
指定されたコマンドを解釈する C のプログラムを考えてみよう。このプログラムにはおそらく、コマンドの表が必要になるだろう。そして、その表はおそらく構造体の配列であり、以下のように宣言されるだろう。
struct command
{
char *name;
void (*function) ();
};
struct command commands[] =
{
{ "quit", quit_command},
{ "help", help_command},
...
};
各コマンド名を二回、文字列定数で一回、関数名で一回指定しているのを止めたほうがきれいだろう。コマンド名を引数として取るマクロを使えば二回指定する必要がなくなる。文字列定数は文字列化により作り出すことができるし、関数名は引数を _command と連結する事により作りだすことができる。以下にその方法を示す。
#define COMMAND(NAME) { #NAME, NAME ## _command }
struct command commands[] =
{
COMMAND (quit),
COMMAND (help),
...
};
連結の普通の使い方は、二つの名前(あるいはひとつの名前とひとつの数)を連結して一個の名前にすることである。しかし、これだけが有効な場合ではない。二つの数(あるいは、一つの数と一つの名前、例えば 1.5 と e3)
を連結して一つの数にすることも可能である。また、例えば +=
のような複数文字からなる演算子も連結により作り出すことができる。ただし、二つのテキスト断片が、一つにつなげても有効な字句単位にならないものであれば、連結は不可能である。例えば、x と + を連結しても意味がない。この二つの文字を組み合わせても、C の字句単位にはならないからである。ANSI 規格は、このような連結を試みた場合の結果は未定義としているが、GNU C プリプロセッサではうまく定義されている。x と + を何の特別な処理も行わずに出力するのである。
C プリプロセッサは、マクロを調べる前にコメントを空白に変換してしまうということに注意しよう。このため、/ と * を連結してコメントを作り出すことはできない。コメントの始まりの/* という列は字句単位ではなく、むしろ、「長い」空白文字の始まりになるのである。また、マクロ定義中の ## の次や、連結される実引数の中にコメントを自由に使うことができる。なぜなら、コメントは一番最初に空白に変換され、連結により後で空白が捨てられるからである。
マクロを未定義にするとは、マクロの定義を取り消すことである。これは、#undef 制御子で行なう。#undef の後に未定義にするマクロ名を置く。
定義の場合と同じように、未定義にするのもソースファイルのある特定の点で起きることであり、その点から適用が開始される。指定した名前はマクロ名で無くなり、その点以降では、あたかもそれがマクロ名であったことなど無かったかのようにプリプロセッサが取り扱う。
例えば、
#define FOO 4 x = FOO; #undef FOO x = FOO;
は、
x = 4; x = FOO;
と展開される。この例では、展開結果が有効な C のコードになるためには、(一時的な)マクロで良かったのと同様に変数や関数でも良かった。
引数付きの定義も引数なしの定義もどちらも、同じ形式の #undef 制御子で取り消すことができる。#undef 制御子を、現在マクロとして定義されていない名前に使っても何の効果もない。
マクロの再定義とは、既にマクロとして定義されている名前を(#define を使って)定義することである。
再定義は、新しい定義が古いものと空白などを除いて等価なら自明であると言われる。わざわざ自明な再定義を書くことはないだろうが、ヘッダファイルが複数回インクルードされていると知らないうちにそのように再定義されることがあるので、黙って受け付けるようになっており、その場合何の効果ももたらさない。
自明でない再定義は、エラーも同然であるとみなされるので、プリプロセッサは警告を出す。だが、途中でマクロ定義を変えたいときもある。再定義についての警告を抑止するには、二回目の定義の前に #undef でそのマクロを未定義にすれば良い。
再定義が自明であるためには、新しい定義は既に有効になっているものに以下の二点を除いて厳密に一致しなければならない。
コメントは空白として勘定されることに注意。
この節では、マクロとマクロ展開に適用される特別な規則を幾つか説明し、直観に反する結果が得られる規則について、幾つかの場合について指摘しておく。これらについて読者は注意しなくてはならない
マクロが引数付きで呼び出されたとき、その引数がマクロ本体に挿入された後、その挿入後の結果が、入力ファイルの残りの部分と合わせて、さらにマクロ呼出しがないか検査されることを思いだそう。
マクロ呼出しは、一部をマクロ本体から、一部を実引数から取って組み合わせて作ることもできる。例えば、
#define double(x) (2*(x)) #define call_with_1(x) x(1)
と成っている場合、call_with_1 (double) を展開すると(2*(1)) となる。
マクロ定義では、括弧が釣り合っている必要はない。マクロ本体で釣り合ってない開き括弧を書いて、マクロ本体内で始まり、外側で終わるマクロ呼出しを作り出すことも可能である。例えば、以下のようになる。
#define strange(file) fprintf (file, "%s %d", ... strange(stderr) p, 35)
この変な例は、fprintf (stderr, "%s %d", p, 35) と展開されるのである。
これまで示したマクロの定義例は、ほとんどの場合、マクロの引数名が現れる度にそれを括弧で囲んでいたことに気付いた方もいるだろう。さらに、マクロ定義全体も通常は括弧で囲んでいた。以下に、そう言う風にマクロを書くのが何故最善の方法なのかを示す。
以下のように、あるマクロを定義したとしよう。
#define ceil_div(x, y) (x + y - 1) / y
このマクロの目的は、切り上げ割り算を行うことである。(この演算の使い道の一つには、ある一定の数の char 型のオブジェクトを保持するのに int 型オブジェクトがいくつ必要かを計算するというのがある。) そして、以下のように使ったとしよう。
a = ceil_div (b & c, sizeof (int));
これを展開すると以下のようになる。
a = (b & c + sizeof (int) - 1) / sizeof (int);
この結果は意図に反する。C 言語の演算子の優先順位の規則により、これは以下と同じだからである。
a = (b & (c + sizeof (int) - 1)) / sizeof (int);
だが、意図としては次のようなものが欲しかったのである。
a = ((b & c) + sizeof (int) - 1)) / sizeof (int);
この場合、マクロを
#define ceil_div(x, y) ((x) + (y) - 1) / (y)
と定義すれば望みの結果が得られる。
意図に反した形で結合が起きるもう一つの場合がある。sizeof ceil_div(1, 2) を考えてみよう。一見すると、ceil_div (1, 2) の型の大きさを計算する C の式のように見えるが、実際は全く別のものを意味する。これは以下のように展開される。
sizeof ((1) + (2) - 1) / (2)
これは、ある整数の大きさを取り、次にそれを 2 で割ることになる。優先順位規則により、割り算が sizeof の外側で行われることになる。sizeof の内側に置くつもりだったものがである。
マクロ定義全体を囲む括弧を置くと、そういう問題を避けることが出来る。以下に、ceil_div の推奨される定義方法を示す。
#define ceil_div(x, y) (((x) + (y) - 1) / (y))
マクロが複文に展開されるように定義したい場合がある。例えば、次のマクロを考えてみよう。これは、空白文字の先までポインタ(引数 p で指定される)を進めるものである。
#define SKIP_SPACES(p, limit) \
{ register char *lim = (limit); \
while (p != lim) { \
if (*p++ != ' ') { \
p--; break; }}}
ここではバックスラッシュ-改行を使って、本来一行でなければならないマクロ定義を分割している。これは、もしこれがマクロ定義の一部でなかったら、C のコードはこのように配置されるであろう形に似せているためである。
このマクロへの呼出しは、SKIP_SPACES (p, lim) のようになるだろう。厳密に言えば、この呼出しは複文に展開されるので、最後にセミコロンを付ける必要のない完全な文である。だが、見かけは関数呼出しのようである。このため、関数呼出しのように使えるなら、混乱を最小限に押さえるために、SKIP_SPACES (p, lim); のように、最後にセミコロンを付ければよい。
だが、これが else 文の直前にあると問題になるのである。この場合、セミコロンは実際にはヌル文になるからである。以下のように書いたとしよう。
if (*p != 0) SKIP_SPACES (p, lim); else ...
二つの文-複文とヌル文-が、if 条件節と else の間に存在するために、C としては正しくないコードになっている。
マクロ SKIP_SPACES の定義を、do ... while 文を使うように変えれば、この問題を解決できる。すなわち、次のようにする。
#define SKIP_SPACES(p, limit) \
do { register char *lim = (limit); \
while (p != lim) { \
if (*p++ != ' ') { \
p--; break; }}} \
while (0)
今度は、SKIP_SPACES (p, lim); は次のように展開される。
do {...} while (0);
これなら、一個の文である。
多くの C プログラムで最小値を求めるマクロ min を以下のように定義している。
#define min(X, Y) ((X) < (Y) ? (X) : (Y))
このマクロを以下のように副作用のある引数に対して使うと、
next = min (x + y, foo (z));
以下のように展開される。
next = ((x + y) < (foo (z)) ? (x + y) : (foo (z)));
ここで、x + y は X が置き変わったものであり、foo (z) は Y が置き変わったものである。
関数 foo は、プログラムの見た目では、この文では一回しか使われていない。だが、foo (z) はマクロの展開形では二回置き換えられている。この結果、foo はこの文が実行されると二回呼び出される。この関数に副作用があったり、計算時間が長くかかると、その結果は意図したものでなくなる可能性がある。我々は min を危険なマクロと呼ぶ。
この問題を解決する最善の方法は、foo (z) の値を一回しか計算しないように min を定義することである。C 言語には標準ではそのための方法がないが、GNU C の拡張を使えば以下のようにして解決できる。
#define min(X, Y) \
({ typeof (X) __x = (X), __y = (Y); \
(__x < __y) ? __x : __y; })
GNU C の拡張は使いたくないということであれば、min マクロを使うときに注意するしかない。例えば、foo (z) の値を計算したら、それを変数に取っておき、その変数を min で使う。
#define min(X, Y) ((X) < (Y) ? (X) : (Y))
...
{
int tem = foo (z);
next = min (x + y, tem);
}
(ここでは foo の戻り値型は int と仮定している)。
自己参照マクロとは、その名前がその定義の中に現れるものである。ANSI C 規格の特別な機能として、自己参照はマクロ呼び出しとは見なされない。プリプロセッサの出力に変更なしで渡される。
次の例を考えてみよう。
#define foo (4 + foo)
ここで foo は読者のプログラムでは変数にもなっているとする。
通常の規則に従えば、foo に対する参照はそれぞれ(4 + foo) に展開される。次にこれが再走査され、(4 + (4 + foo))
に展開される。これが、メモリを使い果たしてプリプロセッサが致命的エラーを起こすまで続くだろう。
ただし、自己参照についての特別な規則により、この過程は、一回終わったところ、(4 + foo) になったところで打ち切られる。このため、このマクロ定義は、foo が参照されたところではどこでもfoo の値に 4 を加えるという役に立つかもしれない効果をプログラムに与える。
ほとんどの場合は、この機能を利用するのは間違った考えである。プログラムを読んで foo が変数であることを知った人は、それが同時にマクロでもあるとは思わないだろう。プログラムを読む人は、foo という識別子がプログラムにあるのに気が付いたら、その値は変数 foo の値であるはずと思うだろう。一方、実際にはその値は 4 大きいのである。
自己参照についての特別な規則は 間接自己参照にも適用される。これは、あるマクロ x がマクロ y を使うよう展開され、y を展開するとマクロ x を参照するという場合である。結果として x への参照は x の展開から間接的に由来するので、これは自己参照になり、これ以上は展開されない。つまり、
#define x (4 + y) #define y (2 * x)
とすると、x は (4 + (2 * x)) に展開される。いいかな?
だが、y が、x の定義からではなくて、どこか別の所で使われている場合を考えてみよう。そうすると、y の展開中でx を使うのは自己参照ではない。x が「現在進行中」ではないからである。このため、展開が行われる。しかし、x の展開にはy への参照が含まれ、今度は間接自己参照になる。y は「現在進行中」だからである。この結果、y は(2 * (4 + y)) に展開される。
こういう動作が一体役に立つのかどうか定かではないが、ANSI C 規格で規定されているので、これを理解する必要が出てくるかもしれない。
実引数の置き換えを含む、マクロ展開は、さらに展開すべきマクロ呼出しがないか再走査されるということを説明した。
実際に起きていることはもっと巧妙である。最初に各実引数テキストが独立にマクロ呼出しがないかどうか走査される。次に、その結果がマクロ本体に代入されマクロ展開を生成する。そして、そのマクロ展開に展開すべきマクロがないかどうか再走査されるのである。
この結果、実引数はその中にあるマクロ呼出しを展開するために 二回 走査されるのである。
ほとんどの場合は、何も影響がない。実引数が任意のマクロ呼出しに含まれていれば、最初の走査の時に展開される。その結果、マクロ呼出しがなくなるので、二回目の走査では変更を行わない。実引数が与えられたように代入され、事前走査が行われないのなら、残り一回の走査で同じマクロ呼出しを見つけて同じ結果をせいせいする。
読者は、自己参照マクロが別のマクロの実引数の中で使われているときは、二重に走査を行うと結果が変わると期待するかもしれない。自己参照マクロが最初の走査で一回展開され、二回目の走査でもう一度展開されると。だが、こうはならないのである。最初の走査で展開されない自己参照は、印が付けられ、二回目の走査でも展開されないのである。
事前走査は、引数が文字列化されたり連結されたりする場合には行われない。すなわち、
#define str(s) #s #define foo 4 str (foo)
は "foo" に展開される。再び、事前走査による意味のある影響は防がれる。
もっと正確に言うなら、文字列化と連結では、書かれた通りの引数を事前走査無しに使うのである。同じ実引数が、文字列化や連結を受けることなくどこか他の場所で置換されるなら、事前走査を受けた形で使われる。
#define str(s) #s lose(s) #define foo 4 str (foo)
expands to "foo" lose(4).
さて読者はこう聞きたくなるだろう。「何で事前走査の話を持ちだしたんだい? なんの違いもないんだろ? それに、どうして事前走査を飛ばしてプリプロセッサをもっと速くするようにしないの?」答えは、事前走査は、三つの特別な場合には違いが生じるからである。
あるマクロの実引数にそのマクロそのものへの呼出しが含まれる場合、入れ子のマクロ呼出しが起きたと言う。例えば、f が引数を一つ取るマクロの場合、f (f (1)) は、入れ子になったf の呼出しの対である。望ましい展開は、f (1) を展開し、それを f の定義の中に代入することである。事前走査があるので、期待どおりの結果になる。事前走査がないと、f (1) 自身が実引数として代入されるので、内側の f は本番の走査のときに間接的な自己参照と見なされ、展開されないのである。ここでは、事前走査により、自己参照マクロの特別な規則の、望ましくない副作用(この場合、医学的な意味で言っており、計算機学的なものではない)が打ち消されるのである。
だが、事前走査は、入れ子マクロ呼出しの他の場合に問題を起こすことがある。以下に例を示す。
#define foo a,b #define bar(x) lose(x) #define lose(x) (1 + (x)) bar(foo)
bar(foo) は (1 + (foo)) に展開して欲しくて、次には(1 + (a,b)) に展開して欲しいと思う。だが、代わりに、bar(foo) は lose(a,b) に展開され、lose の引数は一個なのでエラーになる。この場合は、算術演算の入れ子の間違いを防ぐのに使うように括弧を使えば、簡単に解決できる。
#define foo (a,b) #define bar(x) lose((x))
マクロの引数が式でない場合は問題はもっと深刻になる。その場合は、括弧を付けると正しくない C のコードになってしまうからである。
#define foo { int a, b; ... }
GNU C では、({...}) 構文を使ってカンマを保護することができる。この構文は、複文を式に変える。
#define foo ({ int a, b; ... })
あるいは、マクロ定義を書き直してこのようなカンマを避けることもできる。
#define foo { int a; int b; ... }
もう一つ、事前走査が役に立つ場合がある。事前走査で引数を展開してからそれ文字列化することが、二段階のマクロを使うことで、可能になる。上に出てきた例に新しいマクロ xstr を追加しよう。
#define xstr(s) str(s) #define str(s) #s #define foo 4 xstr (foo)
マクロの カスケード とは、一つのマクロ本体に他のマクロへの参照が含まれているものである。
#define BUFSIZE 1020 #define TABLESIZE BUFSIZE
これは、TABLESIZE を 1020 と定義するのと全く同じではない。TABLESIZE という #define は、指定したマクロ本体をそのまま使う。この場合、BUFSIZE である。そして、それもまたマクロ名になっているかどうかの検査は行なわない。
TABLESIZE を展開した結果にさらにマクロ名が含まれるかどうかの検査が行なわれるのは、TABLESIZE を使った時だけである。
これは、BUFSIZE の定義をソースファイルの途中で変更したときに違いが出てくる。以下のように定義した場合、TABLESIZE は、常にその時点で有効な BUFSIZE の定義を使って展開される。
#define BUFSIZE 1020 #define TABLESIZE BUFSIZE #undef BUFSIZE #define BUFSIZE 37
こうすると、TABLESIZE は(二段階で) 37 に展開される。(#undef は、BUFSIZE の自明でない再定義について警告を避けるためである。)
従来のマクロ処理では、マクロ引数の中の改行を全てマクロの展開形にそのまま持ち込んでいた。つまり、引数のどれかが複数回置換されたり、あるいは全く置換が起きなかったり、順番が変わってしまうと、展開後改行が重複したり、失われたり、移動したりしてしまうのである。展開形が複数の文からなっている場合、この効果により、これらの文のうちのいくつかの行番号がずれてしまう。その結果、エラーメッセージの中や、デバッガが表示する行番号が正しくないものになるのである。
GNU C プリプロセッサは ANSI C モードで動作するときは、引数を複数使った場合に適切に調整を行う。一回目に使ったときは、全ての改行を展開し、同じ引数を使ったとき二回目以降は、改行を出さない。ただし、このモードでも、引数の使われる順番が変わったり、全く使われなかったりする場合は正しくない行番号が生成される可能性がある。
以下はこの問題を説明する例である。
#define ignore_second_arg(a,b,c) a; c
ignore_second_arg (foo (),
ignored (),
syntax error);
syntax error というトークンにより引き起こされる文法エラーに対し、4行目にエラーがあるというメッセージが出ることになる。問題のテキストは 5 行目にあるのだが。
マクロ・プロセッサにおいては、条件節(conditional) とは、プログラムのある部分をコンパイルの際に条件によって無視することを可能にする制御子のことである。C プリプロセッサでは、条件節では、算術式か、あるいはある名前がマクロとして定義されているかどうかをテストすることができる。
C プリプロセッサの条件節は、幾つかの点で C の if 文に似ているが、違いについて理解することが重要である。if 文の条件は、プログラムの実行中にテストされる。その目的は、処理対象のデータにより、実行するたびにプログラムの動作が異なること可能にすることにある。前処理条件節制御子での条件は、プログラムがコンパイルされるときにテストされる。その目的は、コンパイル時の状況におりプログラムに異なるコードを含めることを可能にすることにある。
一般に、条件節を使う理由には三つの種類がある。
一つの機種でしか実行するつもりのない最も単純なプログラムなら、前処理条件節を使う必要はないだろう。
C プリプロセッサの条件節は、条件節制御子 で始まる。これには、#if、#ifdef、#ifndef という種類がある。#ifdef と #ifndef については See Conditionals-Macros。ここでは #if だけ説明する。
#if Directive#if 制御子の一番単純な形は以下のようになる。
#if expression controlled text #endif /* expression */
#endif の後のコメントは必須ではないが、これを付けておくのは良い習慣である。#endif がどの #if に対応するかを見る助けとなるからである。このようなコメントは、入れ子になっていない短い条件節を除き、常に書くべきである。実際、#endif の後には何を置いても良く、GNU C プリプロセッサはそれを無視する。ただし、ANSI C 規格ではコメントしか受け付けないことになっている。
expression は、厳しい制限付きの、C の整数型の式である。これには以下のものが含まれる。
long か unsigned long として認識される。
char を使う。このため、ある文字コードが負になるかどうかは、プリプロセッサをコンパイルするのに使われた C コンパイラにより決まる。そのコンパイラが char を符号付きとして扱うなら、符号ビットが立つほど大きな文字コードは負と見なされる。そうでない場合は、負と見なされる文字コードは存在しない。
&& と ||)。
sizeof 演算子と enum型の値が許されないことに注意。enum 型の値は、マクロ呼び出しや展開として受け付けられない他の全ての識別子同様、ゼロとして扱われる。
条件文の中の controlled text に前処理制御子を入れることもできる。そうすると、この条件節の中の制御子は、その条件の分岐が成功した場合にのみ従う。この場合のテキストには、他の条件節のグループも含めることができる。ただし、#if と #endif は対応が取れていなければならない。
#else Directive#else 制御子をある条件節に追加することで、その条件が偽になったときに使われる別のテキストを用意することができる。これは以下のようなことである。
#if expression text-if-true #else /* Not expression */ text-if-false #endif /* Not expression */
expression がゼロでなければ、text-if-true が使われ、#else は不成立の条件節のように振るまい、text-if-false
は無視される。反対に、#if 条件節が不成立なら、text-if-false
が使われる。
#elif Directive入れ子の条件節の良く使われる場合の一つに、選択肢が 2 つより多い場合の検査がある。例えば、以下のように書くことができる。
#if X == 1 ... #else /* X != 1 */ #if X == 2 ... #else /* X != 2 */ ... #endif /* X != 2 */ #endif /* X != 1 */
もう一つの条件節制御子 #elif を使うとこれが以下のように簡略化できる。
#if X == 1 ... #elif X == 2 ... #else /* X != 2 and X != 1*/ ... #endif /* X != 2 and X != 1*/
#elif は "else if" を表す。#else 同様、一組の #if-#endif の中間に置き、この組をさらに分割する。#elif 自身に対応する #endif は必要でない。#if 同様、#elif 制御子はテストされる式が含まれる。
#elif に続くテキストは、先頭の #if 条件節に合致せず、#elif 条件節が合致した場合にだけ処理される。複数の #elif
を、同一の #if-#endif の中に置くことができる。その場合、それぞれの #elif の後のテキストは、先頭の #if
とその前の #elif 制御子群が合致せず、当該の #elif 条件節が合致したときにだけ処理される。#else は #elif 1 に等価であり、#else は任意の個数の #elif 制御子の後に置くことができる。ただし、#else の後に elif を置くことはできない。
プログラムの一部を置き換えたり、削除したいが、その古いコードを将来の参照用にコメントとして近くに残して置きたいという場合、最も簡単な方法は、該当する部分の前に #if 0を、その後に#endif を置くことである。これは、コメント区切り /* と*/ を使うよりも良い。というのは、後者の方法は対象とするコードが既にコメントを含んでいると使えないからである。(C のコメントはネストできない。)
これは対象とする部分に条件節があっても動作する。ただし、その場合、条件節は丸ごと入っていないといけない(#if と #endif が釣り合っている必要がある)。
逆に、C のコードでないコメントに #if 0 を使ってはいけない。その場合は、代わりに /* と */ を使うこと。#if 0 の中は、完全なトークンからなっていなければならない。特に、一重引用符文字は釣り合っていなければならない。だが、コメントの中には釣り合っていない一重引用符文字が入っていることが多い(英文ではアポストロフィーになる)。これらは #if 0 を混乱させてしまうが、/* なら大丈夫である。
条件節は、マクロや表明と共に使うと役に立つ。何故なら、ある式の値がコンパイル毎に変わりうるとしたら、マクロや表明だけがそれに該当するからである。#if 制御子の式が、マクロや表明を使っていなければ、#if 1 や #if 0
に等価である。式の値を自分で計算して、このどちらになるかを決めれば、プログラムの簡素化になる。
例えば、以下は BUFSIZE == 1020 という式を検査する条件節である。ここで BUFSIZE はマクロでなければならない。
#if BUFSIZE == 1020
printf ("Large buffers!\n");
#endif /* BUFSIZE is large */
(変数やデータ型の大きさを #if で検査できたらと思うプログラマが多いようだが、それはうまくいかない。プリプロセッサは、sizeof やtypedef 名、さらには int のような型を表すキーワードのことさえ理解しないからである。)
#if 式では、特別な演算子 defined を使って、ある名前がマクロとして定義されているかどうかを検査することができる。defined name あるいは defined (name) は、一個の式であり、その値は name がプログラムの現在の点でマクロとして定義されていれば 1 であり、定義されていなければ 0 である。defined 演算子から見れば、マクロの定義がどんなものであるかは関係ない。関係するのは、どこかで定義されているかどうかだけである。つまり、例えば、
#if defined (vax) || defined (ns16000)
は、名前 vax と ns16000 のどちらかがマクロとして定義されていれば成功する。同じ条件を以下のように表明(see Assertions) を使って検査することもできる。
#if #cpu (vax) || #cpu (ns16000)
あるマクロが定義され、後で #undef で未定義になった場合は、その後で defined 演算子を使うと 0 を返す。その名前がもはや定義されていないからである。そのマクロが別の "define により再度定義されたなら、defined は再び 1 を返すようになる。
一つの名前だけを定義されているかどうか検査するのは非常に良く現れるので、その場合向けに特別な短い条件節制御子が二つ用意されている。
#ifdef name
#if defined (name) に同じである。
#ifndef name
#if ! defined (name) に同じである。マクロの定義は、色々な理由によりコンパイルを行なう毎に変わってくる可能性がある。
vax という名前は事前定義マクロである。他の機種ではこのマクロは定義されないだろう。
BUFSIZE が読者のプログラムのコンフィギュレーションファイルで定義されていて、各ソースファイルでヘッダファイルとしてインクルードされているかもしれない。BUFSIZE をプリプロセスの条件節で使って、選択したコンフィギュレーションに依って、異なるコードを生成するようにするだろう。
-D や -U を使って定義したり、未定義にしたりすることができる。同じソースファイルを異なる二つのプログラムにコンパイルされるように設定することができる。それには、必要な方のプログラムを指定するマクロ名を選び、そのマクロが定義されているかどうか、あるいはどのように定義されているかを調べる条件節を書けば良い。そうしておいて、マクロの状態をコンパイラのコマンド行オプションで制御するのである。See Invocation。
表明(assertion)は、コンパイルされるプログラムが動作するコンピュータやシステムの種類を検査するための条件節を書くのに、マクロよりも組織的な方法である。表明は通常事前定義されているが、ユーザもプリプロセッサ制御子やコマンド行オプションで定義することができる。
ターゲットのタイプを記述するのに伝統的に使われていたマクロは、それがどんな質問に対する答えであるかによっては分類されていない。ハードウェアアーキテクチャや、特定のハードウェアモデル、オペレーティングシステム、オペレーティングシステムの特定のバージョン、特定のコンフィギュレーションオプション等を指示する。これが、一つの名前空間にごたまぜになっている。対照的に、表明はそれぞれ、指定された質問と答えから成る。この質問は通常 述語 と呼ばれる。表明は以下のような形になる。
#predicate (answer)
predicate には適切な形の識別子を使わなければならない。answer の値は、単語の任意の列になりうる。全ての文字が意味を持つ。ただし、先行する、あるいは追従する空白は除く。そして、内部の空白列の差異は無視される。つまり、x + y は x+y とは異なるが、x + y には等価である。答えの中に ) を置くことはできない。
次は、答え answer が述語 predicate に対して表明されるかどうかをテストする条件節である。
#if #predicate (answer)
ある指定された述語に対して表明される答えは複数の場合もある。答えを省略すると、任意の答えが predicate で表明されるかどうかをテストすることができる。
#if #predicate
ほとんどの場合、読者がテストする表明は事前定義された表明になるだろう。GNU C は、事前定義述語を三つ提供している。system、cpu、machine である。system はソフトウェアのタイプについての表明用である。cpu はコンピュータアーキテクチャのタイプを記述する。machine はそのコンピュータについてのさらなる情報を提供する。例えば、GNU システムでは、次の表明が真になる。
#system (gnu) #system (mach) #system (mach 3) #system (mach 3.subversion) #system (hurd) #system (hurd version)
他にも真になるものがあるだろう。多少バージョン情報が付け加わった選択肢の方は、システムソフトウェアのタイプについて多少詳細な質問をすることができるようになる。
Unix システムの場合は、#system (unix) とおそらく次のうちの一つがあるだろう。#system (aix)、#system (bsd)、#system (hpux)、#system (lynx)、#system (mach)、#system (posix)、#system (svr3)、#system (svr4)、#system (xpg4)。これにバージョン番号が付くこともある。
system の他の値としては #system (mvs) と#system (vms) がある。
移植性についての注意。多くの Unix の C コンパイラは、表明 system に対し、答えを一つしか用意していない。#system (unix) である。何はともあれ、表明をしているならの話であるが。これでは全く役に立たない。
複数の単語からなる答えを持つ表明は、一語の答えを個々に持つ様々な表明とは全く異なるものである。例えば、system (mach 3.0) があっても system (3.0) が真であることは意味しない。また、system (mach) を直接は意味しない。だが GNU C では、その最後が普通は同様に表明される。
現在、cpu 用の表明値として可能なものは以下の通りである。#cpu (a29k)、#cpu (alpha)、#cpu (arm)、#cpu
(clipper)、#cpu (convex)、#cpu (elxsi)、#cpu
(tron)、#cpu (h8300)、#cpu (i370)、#cpu (i386),
#cpu (i860)、#cpu (i960)、#cpu (m68k)、#cpu
(m88k)、#cpu (mips)、#cpu (ns32k)、#cpu (hppa),
#cpu (pyr)、#cpu (ibm032)、#cpu (rs6000),
#cpu (sh)、#cpu (sparc)、#cpu (spur)、#cpu
(tahoe)、#cpu (vax)、#cpu (we32000)。
C プログラムの中で #assert を使って表明を作ることができる。以下のようにする。
#assert predicate (answer)
(predicate の前に # が無いことに注意。)
これを行う度に、predicate に対する新しい真の答えを表明することになる。一つの答えを表明しても、以前に表明された答えを無効にすることはない。それらは全て真のまま残る。ある表明を削除するには #unassert を使うしかない。#unassert の構文は #assert に同じである。predicate に関する表明を全て削除するには以下のようにすれば良い。
#unassert predicate
gcc や cpp を実行するときにも、コマンド行オプションを使って表明を追加したり取り消したりすることができる。
#error and #warning Directives制御子 #error を使うと、プリプロセッサに致命的エラーを報告させることができる。#error 以降行末までがエラーメッセージとして使われる。この行は完全なトークンからなっていなければならない。
条件節の中で #error を使って、そのプログラムが正しくサポートしていないパラメータの組合せを検出することができる。例えば、プログラムが VAX 上では正しく動作しないことを知っているなら、次のように書くことができる。
#ifdef __vax__ #error "Won't work on Vaxen. See comments at get_last_object." #endif
この動作の仕組みについては See Nonstandard Predefined。
コンフィギュレーションのパラメータがたくさんあり、それがインストールの際に矛盾のないように設定されなければならないのなら、条件節を使って矛盾があれば検出して、#error を使って報告するようにすることができる。
#if HASH_TABLE_SIZE % 2 == 0 || HASH_TABLE_SIZE % 3 == 0 \
|| HASH_TABLE_SIZE % 5 == 0
#error HASH_TABLE_SIZE should not be divisible by a small prime
#endif
制御子 #warning は #error に似ているが、プリプロセッサに警告を出させた後、プロプロセス処理を続行させる。#warning の行の残りの部分は、警告メッセージとして使われる。
古いヘッダファイルに #warning を書いておいて、代わりに使うべきヘッダファイルをユーザに指し示すメッセージを出すという使い方もある。
C プリプロセッサの仕事の一つに、C コンパイラに、C のコードの各行が元はどこから来たのか、つまりソースファイルと行番号を知らせるということがある。
C のコードは、#include を使っていれば、複数のソースファイルから来る可能性がある。#include も条件文やマクロを使うこともどちらもプリプロセッサの出力中のある行の行番号を、元のソースファイルでの行番号とは異なるものにする。C コンパイラ(のエラーメッセージで)とGDB のようなデバッガが、どちらも、ソースファイルでの行番号を使うようにすることの価値は理解して貰えると思う。
C プリプロセッサは、この機能をある制御子を使って実現している。この制御子を使うとこの機能を明示的に制御することができる。これは、C プリプロセッサに対する入力ファイルが、何か別のプログラム、たとえばパーザ生成器 bison 等の出力である場合に役に立つ。この場合、その別のプログラムは、本来のソースファイルである別のファイルに対して操作を行うものである。bison の出力の一部はゼロから作られるものであり、また別の一部は標準のパーザファイルから取られる。それ以外の部分は、ソースファイルからほぼそのままコピーされるが、bison の出力の行番号と元々の行番号では同じでない。当然のことながら、コンパイラのエラーメッセージやシンボリックデバッガでは、元々のソースファイルとbison の入力の各行の元々の行番号を使ったほうが好ましいはずである。
bison は、#line 制御子を出力ファイルに書き出すことでこれを調整する。#line は、現在のプリプロセッサ入力ファイル中の、引き続く入力に対する元々の行番号とソースファイル名を指定する制御子である。#line には三種類の形式がある。
#line linenum
#line linenum filename
#line anything else
#line 制御子は、その点以降、事前定義マクロ __FILE__ と__LINE__ の結果を変える。See Standard Predefined。
プリプロセッサの出力(それはコンパイラの残りのフェーズに対する入力である)には、#line 制御子に良く似た制御子が含まれる。出力の方は、#line の代わりに単なる # で始まるが、その後には #line の場合と同じ行番号とファイル名が続く。See Output。
この節では三つの付加的な前処理制御子を説明する。そんなに役に立つものではないが、完全を記すために触れておく。
ヌル制御子は、一個の # の後に改行が一個続いたもので、その間に置けるのは空白(コメントを含む)だけである。ヌル制御子は、前処理制御子として認識はされるが、プリプロセッサの出力には何の影響も与えない。ヌル制御子の第一の存在意義は、# だけの行が、# を含む行をそのまま出力させずに、何も出力させないことにある。古い C プログラムの中にはそういう行が含まれていると言われている。
ANSI 規格では、#pragma 制御子の効果を実装定義と規定している。GNU C プリプロセッサでは、#pragma once(see Once-Only)を除いて、#pragma 制御子は使われない。だが、プリプロセッサの 出力にはそのまま残り、コンパイルパスで利用できる。
#ident 制御子はある種の他のシステムとの互換性のためにサポートされている。この制御子の後には、ある一行のテキストが続く。機種によってはこのテキストがオブジェクトファイルの中の特別な位置にコピーされる。ほとんどのシステムではこのテキストは無視され、この制御子の効果は何もない。普通は、#ident は、それが意味を持つシステムで提供されているヘッダファイルでしか使われない。
C プリプロセッサの出力はその入力とほとんど同じであるが、違いは、全ての前処理制御子行は空行に置き換えられ、全てのコメントが空白に置き換えられる点にある。ある一行内にある空白は変更を受けない。だが、-traditional を指定しない限り、マクロ呼びだしの展開に空白が挿入されるために、トークンの連結を妨げる。
ソースのファイル名と行番号の情報は以下の形の行により、伝えられる。
# linenum filename flags
この行は、必要に応じて入力の途中に挿入される(文字列定数あるいは文字定数の中に挿入されることはない)。この形の行の意味するところは、続く行が元は、ファイル filename の linenum 行目にあったことを意味する。
ファイル名の後にゼロ個以上のフラグが来る。このフラグは、1、2、3、4 のどれかである。複数のフラグを指定するときは空白で区切る。以下にフラグの意味を示す。
1
2
3
4
C プリプロセッサを使うとき、ほとんどは直接起動することはないだろう。C コンパイラが自動的に起動する。だが、プリプロセッサ単体で使うこともある。
C プリプロセッサは、引数としてファイル名を二つ、infile とoutfile を取ることを想定している。プリプロセッサは、infile と #include により指定されるファイルを読み込む。入力ファイルの組合せから生成された出力は全て outfile に書き込まれる。
infile あるいは outfile は - でも良い。こうすると、infile は標準入力から読み込むことを意味し、outfile は標準出力に書き出すことを意味する。また、outfile あるいは両方のファイル名を省略すると、標準出力と標準入力が省略されたファイル名の代わりに使われる。
以下に、C プリプロセッサが受け付けるコマンド行オプションの表を示す。ここに示したオプションは、C プログラムをコンパイルする際にも指定可能である。これは、コンパイラから起動されたプリプロセッサに自動的に渡される。
-P
#-行を生成することを禁止する(see Output)。何か C のコードではないものに対してプリプロセッサを実行し、その結果を、#行があると混乱するかもしれないプログラムに送る時に役に立つだろう。
-C
-traditional
1.0e+4 は、三つのトークン 1.0e、+、4 からなると考えていた。
# はマクロ定義内で何の特別な意味も持たなかった。
\ があると、その直後の文字の文法的に特別な意味を打ち消す。Fortran のコードを前処理するときは -traditional オプションを使って、Fortran のコメント行にあるシングル・クォートやダブル・クォートのために文字定数や文字列定数の終りがない旨の診断メッセージを出さないようにしよう。
ただし、このオプションは、Fortran 形式のコメントの中で、C 形式のコメントが始まっているが、終りがない場合の、コメントの終りがないという旨の診断メッセージは抑止しない。
このため、次の Fortran のコメント行は -tradtional を指定すれば受け付ける。
C This isn't an unterminated character constant C Neither is "20000000000, an octal constant C in some dialects of Fortran
だが、次の形のコメント行は診断メッセージを出すか、あるいは少なくとも終りのないコメントのためにプリプロセッサの出力が予期しないものになるだろう。
C Some Fortran compilers accept /* as starting C an inline comment.
g77 は、プリプロセッサを起動するときに自動的に-traditional オプションを補充する。ただし、将来のバージョンの g77 は、cpp の代わりに、別の、Fortran 向けプリプロセッサを使う可能性がある。
-trigraphs
?? で始まる。ANSI C により定義されており、三文字である一つの文字を表す。例えば、??/ は \ を表す。つまり、'??/n' とすれば、改行を表す文字定数となる。厳密にいうなら、-trigraphs を指定しない限り、GNU C プリプロセッサは ANSI 標準 C で書かれている全てのプログラムをサポートしているとは言えないことになる。だが、その違いがわかれば気にする必要がないということがわかって安心すると思う。
トリグラフについてこれ以上知る必要はない。
-pedantic
#else や #endif の後ろに書かれた場合などである。
-pedantic-errors
-pedantic の警告の代わりにエラーとする。
-Wtrigraphs
-trigraphs オプションまたは -ansi オプションを指定して、トリグラフ機能を有効にしている場合にしか動作しない。将来この制限は取り除かれる予定である。
-Wcomment
/* が、/* 形式のコメントの中に現れた場合、バックスラッシュ-改行列が // 形式のコメントにあらわ場合に警告を出す。
-Wall
-Wtrigraphs と -Wcomment の両方を要求する。-Wtraditional や -Wundef は要求しない。
-Wtraditional
-Wundef
#if 制御子で評価された場合、警告する。
-I directory
-I オプションを複数指定すると、指定されたディレクk取りは左から右側に走査される。標準のシステムディレクトリは後に来る。
-I-
-I- オプションの前で、-I オプションで指定したディレクトリは#include "file" の形式の場合だけ検索される。#include <file> の形式の場合は検索されない。
-I- の後で、-I オプションで指定したディレクトリは、全ての形式の #include で検索される。
さらに、-I- を指定すると、#include "file" の場合に最初に検索するディレクトリとして、カレントディレクトリを使わない。このため、カレントディレクトリは、-I. と明示的に要求した場合にだけ検索される。-I- と -I. を両方組み合わせると、カレントディレクトリの前に検索されるディレクトリと後で検索されるディレクトリを厳密に指定することができる。
-nostdinc
-I オプションで指定したディレクトリ(それに、適切ならカレントディレクトリ)だけを検索する。
-nostdinc++
-remap
header.gcc という名前のファイルがあると、ファイル名の対応を変更する。これは、ファイル名に制限のあるファイルシステムの制限を回避するのに使われる。 ファイル header.gcc の各行は、二つのトークンから成る。最初のトークンが置き換えるべき名前であり、第二のトークンが実際に使われる名前になる。
-D name
1 に、事前定義する。
-D name=definition
-D を 2 回以上使った場合は、最も右側の定義が有効になる。
-U name
-U と -D の両方が指定された場合は、-U が有効になり、名前は事前定義されない。
-undef
-gcc
gcc -E を使うときは、これらは自動的に定義される。その場合に、未定義にするには -no-gcc を指定する。
-A predicate(answer)
-A- を使うと、全ての定義済み表明を無効にできる。また、これは全ての定義済みマクロとコマンド行でこのオプションに先行する全てのマクロを未定義にする。
-dM
#define 制御子のリストを出力する。これにより、読者のバージョンのプリプロセッサで何が定義済みかを知ることができる。foo.h というファイルが存在しない場合に、
touch foo.h; cpp -dM foo.h
というコマンドを実行すると、定義済みマクロの値を表示する。
-dD
-dM に同じである。こちらは、定義済みマクロを含めない。そして、#define 制御子と前処理の結果の両方を出力する。どちらの種類の出力も標準出力ファイルに出力される。
-dI
#include 制御子を出力する。
-M [-MG]
make 向けのルールを出力する。プリプロセッサは、そのソースファイルに対応するオブジェクトファイル名、コロン、全てのそれからインクルードされているファイル名からなるmake のルールを一つ出力する。インクルードされているファイルが多ければ、そのルールは\-改行 により複数行に分けられる。
-MG は、見当たらないヘッダファイルを生成されるファイルとして取扱い、このソースファイルと同じディレクトリにあると仮定する。これは、-M に追加する形で指定しなければならない。
この機能を使って、Makefile の自動更新が行なわれる。
-MM [-MG]
-M と同様だが、#include "file" の形式でインクルードされるファイルだけを出力する。#include <file> の形式でインクルードされるシステムのヘッダファイルは除かれる。
-MD file
-M と同様だが、依存関係の情報は file に書き出される。これは、指定された通りにファイルをコンパイルするのに加えて、行なわれる。-M だと通常のコンパイルを行なわないが、-MD は行なうのである。
gcc を起動するときは、引数の file は指定しないこと。gcc は、入力ファイル名の最後の ".c" を ".d" に置き換えた名前のファイルを作る。
Mach では、md というユーティリティを使って、複数の依存関係ファイルを一個の依存関係にマージすることができ、make コマンドで使うのに適したものになる。
-MMD file
-MD と同様だが、ユーザのヘッダファイルのみを出力し、システムのヘッダファイルは出力しない。
-H
-imacros file
-imacros file の唯一の効果は、file でマクロを定義し、正規の入力でそれを使えるようにすることである。
-include file
-idirafter dir
-I が加えたもの)にあるディレクトリには見つからないときに、検索される。
-iprefix prefix
-iwithprefix オプションのプレフィックスとして指定する。
-iwithprefix dir
-iprefix で指定されたものである。
-isystem dir
-x c
-x c++
-x objective-c
-x assembler-with-cpp
.c、.cc、.m、.S がある。その他の、C++ やアセンブリ言語として一般的な拡張子も認識する。cpp が拡張子を認識できなかったときは、そのファイルを C として扱う。これが最も一般的なモードである。
注意: 以前のバージョンの cpp は -lang オプションを受け付けていた。このオプションは、言語と規格への準拠レベルの両方を選択するものであった。このオプションは削除された。-l オプションとぶつかるからである。
-std=standard
-ansi
standard may be one of:
iso9899:1990
iso9899:199409
c89
c89 はこのバージョンの規格を表す習慣的な省略形である。
-ansi オプションは -std=c89 に同じである。
iso9899:199x
c9x
x となっている。
gnu89
gnu9x
-Wp,-lint
lint のコマンドを探しだし、#pragma lint を前に置いて出力する。例えば、/* NOTREACHED */ というコメントは#pragma lint
NOTREACHED
になる。
-l とぶつかるので、上のような使いにくい書き方をしなければならない。将来のリリースでは、このオプションは -flint か -Wlint で置き換える予定である。まだどちらになるかはわからない。
-$
$ を使うことを禁ずる。C の標準規格はこれを許していないが、一般的な拡張である。
##: Concatenation
#include: Include Syntax
#assert: Assertions
#cpu: Assertions
#define: Argument Macros
#elif: #elif Directive
#else: #else Directive
#error: #error Directive
#ident: Other Directives
#if: Conditional Syntax
#ifdef: Conditionals-Macros
#ifndef: Conditionals-Macros
#import: Once-Only
#include: Include Syntax
#include_next: Inheritance
#line: Combining Sources
#machine: Assertions
#pragma: Other Directives
#pragma once: Once-Only
#system: Assertions
#unassert: Assertions
#warning: #error Directive
-$: Invocation
-A: Invocation
-ansi: Invocation
-C: Invocation
-D: Invocation
-dD: Invocation
-dI: Invocation
-dM: Invocation
-gcc: Invocation
-H: Invocation
-I: Invocation
-idirafter: Invocation
-imacros: Invocation
-include: Invocation
-iprefix: Invocation
-isystem: Invocation
-iwithprefix: Invocation
-lint: Invocation
-M: Invocation
-MD: Invocation
-MM: Invocation
-MMD: Invocation
-nostdinc: Invocation
-nostdinc++: Invocation
-P: Invocation
-pedantic: Invocation
-pedantic-errors: Invocation
-remap: Invocation
-std: Invocation
-traditional: Invocation
-trigraphs: Invocation
-U: Invocation
-undef: Invocation
-Wall: Invocation
-Wcomment: Invocation
-Wtraditional: Invocation
-Wtrigraphs: Invocation
-Wundef: Invocation
-x assembler-with-cpp: Invocation
-x c: Invocation
-x objective-c: Invocation
__BASE_FILE__: Standard Predefined
__CHAR_UNSIGNED__: Standard Predefined
__cplusplus: Standard Predefined
__DATE__: Standard Predefined
__FILE__: Standard Predefined
__GNUC__: Standard Predefined
__GNUC_MINOR__: Standard Predefined
__GNUG__: Standard Predefined
__INCLUDE_LEVEL_: Standard Predefined
__LINE__: Standard Predefined
__OPTIMIZE__: Standard Predefined
__REGISTER_PREFIX__: Standard Predefined
__STDC__: Standard Predefined
__STDC_VERSION__: Standard Predefined
__STRICT_ANSI__: Standard Predefined
__TIME__: Standard Predefined
__USER_LABEL_PREFIX__: Standard Predefined
__VERSION__: Standard Predefined
_AM29000: Nonstandard Predefined
_AM29K: Nonstandard Predefined
BSD: Nonstandard Predefined
defined: Conditionals-Macros
M68020: Nonstandard Predefined
m68k: Nonstandard Predefined
mc68000: Nonstandard Predefined
ns32000: Nonstandard Predefined
pyr: Nonstandard Predefined
sequent: Nonstandard Predefined
sun: Nonstandard Predefined
system header files: Header Uses
unix: Nonstandard Predefined
vax: Nonstandard Predefined