これは (文書化されていない) 「GNU 拡張機能」のようです:[訂正 :やっとドキュメントに言及が見つかりました。以下を参照してください。]
次のコマンドは -dM
を使用しています すべてのプリプロセッサ定義を出力するオプション。入力「ファイル」は空であるため、事前定義されたマクロが正確に表示されます。標準のubuntuインストールでgcc-4.7.3で実行されました。プリプロセッサが標準対応であることがわかります。合計で、-std=gnu99
の 243 個のマクロがあります -std=c99
で 240;関連性のために出力をフィルタリングしました。
$ cpp --std=c89 -dM < /dev/null | grep linux
#define __linux 1
#define __linux__ 1
#define __gnu_linux__ 1
$ cpp --std=gnu89 -dM < /dev/null | grep linux
#define __linux 1
#define __linux__ 1
#define __gnu_linux__ 1
#define linux 1
$ cpp --std=c99 -dM < /dev/null | grep linux
#define __linux 1
#define __linux__ 1
#define __gnu_linux__ 1
$ cpp --std=gnu99 -dM < /dev/null | grep linux
#define __linux 1
#define __linux__ 1
#define __gnu_linux__ 1
#define linux 1
「gnu 標準」バージョンも #define unix
. (c11
を使用 および gnu11
同じ結果が得られます。)
彼らには理由があると思いますが、gcc のデフォルトのインストールを行うように思えます (これは C コードを -std=gnu89
でコンパイルします)。 特に明記しない限り) 不適合、そして -- この質問のように -- 驚くべきことです。名前がアンダースコアで始まらないマクロでグローバル名前空間を汚染することは、準拠した実装では許可されていません。 (6.8.10p2:「他の定義済みマクロ名は、先頭のアンダースコアで始まり、大文字または 2 番目のアンダースコアが続くものとする」が、付録 J.5 (移植性の問題) で述べたように、そのような名前はしばしば定義済みです。)
私が最初にこの回答を書いたとき、この問題に関するドキュメントをgccで見つけることができませんでしたが、C実装定義の動作でもC拡張でもなく、 cpp
マニュアルのセクション 3.7.3 には、次のように記載されています:
予約済みの名前空間の外にあるすべての定義済みマクロを段階的に廃止しています。新しいプログラムでは絶対に使用しないでください…
昔 (ANSI 以前) では、unix
などのシンボルを事前に定義していました。 そして vax
コンパイル時にどのシステム用にコンパイルされているかをコードが検出できるようにする方法でした。当時は (K&R の初版の後ろにある参照資料を超えて) 公式の言語標準はなく、複雑な C コードは通常 #ifdef
の複雑な迷路でした。 ■ システム間の違いを許容する。これらのマクロ定義は通常、コンパイラ自体によって設定され、ライブラリ ヘッダー ファイルでは定義されていません。実装で使用できる識別子とプログラマー用に予約されている識別子に関する実際の規則がなかったため、コンパイラの作成者は unix
のような単純な名前を自由に使用できました。 そして、プログラマーは自分たちの目的のためにそれらの名前を使用することを単に避けるだろうと想定しました.
1989 年の ANSI C 標準では、実装が合法的に事前定義できるシンボルを制限する規則が導入されました。コンパイラによって事前定義されたマクロは、2 つのアンダースコアで始まる名前、またはアンダースコアの後に大文字が続く名前のみを持つことができ、プログラマはそのパターンに一致せず、標準ライブラリで使用されていない識別子を自由に使用できます。
その結果、unix
を事前定義するコンパイラはすべて、 または linux
int linux = 5;
のようなものを使用する完全に正当なコードのコンパイルに失敗するため、非準拠です。 .
たまたま、gcc はデフォルトでは非準拠です -- しかし、適切なコマンドライン オプションを使用して (合理的に) 準拠させることができます:
gcc -std=c90 -pedantic ... # or -std=c89 or -ansi
gcc -std=c99 -pedantic
gcc -std=c11 -pedantic
詳細については、gcc マニュアルを参照してください。
gcc は将来のリリースでこれらの定義を段階的に廃止する予定なので、それらに依存するコードを記述しないでください。プログラムが Linux ターゲット用にコンパイルされているかどうかを知る必要がある場合は、__linux__
かどうかを確認できます。 が定義されています (gcc またはそれと互換性のあるコンパイラを使用していると仮定します)。詳細については、GNU C プリプロセッサのマニュアルを参照してください。
あまり関係のないことですが、David Korn (そうです、Korn Shell の作成者) による 1987 年の International Obfuscated C Code Contest の「ベスト ワン ライナー」受賞者は、事前定義された unix
を利用しました。 マクロ:
main() { printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);}
"unix"
を出力します 、しかし、マクロ名のスペルとはまったく関係のない理由により.