GCCのCプリプロセッサが単語(小文字) を定数として解釈するのはなぜですか?linux
1
テスト.c:
#include <stdio.h>
int main(void)
{
int linux = 5;
return 0;
}
結果$ gcc -E test.c
(前処理段階後に停止):
....
int main(void)
{
int 1 = 5;
return 0;
}
もちろん、エラーが発生します。
(ちなみに、ファイル#define linux
内には何も入っていませんstdio.h
。)
ベストアンサー1
昔 (ANSI 以前) は、 や などのシンボルを事前定義することunix
で、vax
コンパイル時にコードがどのシステム向けにコンパイルされているかを検出できるようにしていました。当時は公式の言語標準はなく (1978 年の 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はC標準の新しいバージョンをサポートするでしょう。gcc マニュアル詳細については。
unix
gcc は将来のリリースでこれらの定義 ( 、linux
など) を段階的に廃止する可能性があります。そのため、これらに依存するコードを記述しないでください。プログラムが Linux ターゲット用にコンパイルされているかどうかを知る必要がある場合は__linux__
、 が定義されているかどうかを確認できます (gcc またはそれと互換性のあるコンパイラを使用していると仮定)。GNU C プリプロセッサマニュアル詳細については。
あまり関係のない余談ですが、1987年の「ベストワンライナー」受賞者は国際難読化CコードコンテストDavid Korn (そう、Korn Shell の作者です) による次のコードは、定義済みunix
マクロを活用しました。
main() { printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);}
と出力されます"unix"
が、その理由はマクロ名のスペルとはまったく関係ありません。
ここでネタバレは書きたくありません。この記事を読んでいる皆さんには、まず自分でそのコードを理解してみることをお勧めします。でも、本当に諦めたいなら、ここに説明を投稿しました:https://gist.github.com/Keith-S-Thompson/6920347