- (1) define 文
- #defineで定義されるものはすべて C プリプロセッサによって、展開されます。以下のプログラムでは、可読性のためにdefine文を利用しています。
このプログラムはCプリプロセッサによって以下のように書き換えられます。 コメントも削除されます。この状態のもの(*.i中間ファイル)を、実際にcc1コンパイラがコンパイルします。#define SUCCESS 1 /* 成功 */ #define FAIL 0 /* 失敗 */ int do_something(){ if(...){ ....; return SUCCESS; /* 成功 */ } /* 失敗 */ return FAIL; }int do_something(){ if(...){ .... return 1; } return 0; }- (2) include 文
- #includeによって指定されたファイルは、その行に挿入されます。stdio.hなどのヘッダファイルはCプリプロセッサによって挿入され、関数や変数の宣言を実現しています。例えば、hello.cプログラムの場合、中間ファイルhello.iはstdio.hに記述された全てのマクロ展開を行った結果となります(従って、非常に長い)。
<hello.c>
#include <stdio.h> int main( void ) { printf("Hello, World!\n"); }<hello.i>
# 1 "hello.c" # 1 "/usr/include/stdio.h" 1 3 # 1 "/usr/include/features.h" 1 3 # 138 "/usr/include/features.h" 3 # 196 "/usr/include/features.h" 3 # 1 "/usr/include/sys/cdefs.h" 1 3 # 47 "/usr/include/sys/cdefs.h" 3 # 69 "/usr/include/sys/cdefs.h" 3 # 103 "/usr/include/sys/cdefs.h" 3 # 138 "/usr/include/sys/cdefs.h" 3 # 250 "/usr/include/features.h" 2 3 # 1 "/usr/include/gnu/stubs.h" 1 3 # 278 "/usr/include/features.h" 2 3 # 27 "/usr/include/stdio.h" 2 3 # 1 "/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stddef.h" 1 3 # 19 "/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stddef.h" 3 # 61 "/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stddef.h" 3 # 131 "/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stddef.h" 3 typedef unsigned int size_t; # 271 "/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stddef.h" 3 # 283 "/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stddef.h" 3 # 317 "/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stddef.h" 3 # 33 "/usr/include/stdio.h" 2 3 # 1 "/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stdarg.h" 1 3 typedef void *__gnuc_va_list; # 116 "/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stdarg.h" 3 # 202 "/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stdarg.h" 3 # 38 "/usr/include/stdio.h" 2 3 # 1 "/usr/include/bits/types.h" 1 3 # 1 "/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stddef.h" 1 3 # 19 "/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stddef.h" 3 # 61 "/usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stddef.h" 3 (途中、省略) # 1 "hello.c" 2 int main( void ) { printf("Hello, World!\n"); }- (3) 記号定数
- 例えば、__FILE__、__LINE__、__DATE__など、コンパイル時にすでに定義されているものでよく利用されているものを紹介します。前述1.2コンパイラの構成にて概観した、コンパイラオプション-vを用いて表示したコンパイル過程の詳細で、cppに-Dオプションにて引き渡されているパラメータも、この記号定数で使用可能なものです。
__FILE__ : ファイル名を指す以下にそれぞれを表示しているプログラムの例と、プリプロセッサ処理後の結果を示します。
__LINE__ : この行数を示す
__DATE__ : コンパイルしている時刻を示す<test1.c>
#include <stdio.h> /* 標準入出力ヘッダ ファイル */ int main(void){ printf("%s(%d): %s\n", __FILE__, __LINE__, __DATE__); printf("%s(%d): %s\n", __FILE__, __LINE__, __DATE__); printf("%s(%d): %s\n", __FILE__, __LINE__, __DATE__); return 0; }<test1.i>このプログラムをtest1.cに保存し、a.outとしてビルドした後、実行した場合の実行結果は以下のとおりです。 すべての記号定数は、それぞれ置き換わっていることに注目してください。特に__LINE__はその行に合わせて変更されています。ここに挙げた __FILE__ などはプリプロセッサによって特別に扱われる設定値ですので、自分では定義しないでください。
(省略) # 1 "test1.c" 2 int main(void){ printf("%s(%d): %s\n", "test1.c", 4, "Dec 12 2001"); printf("%s(%d): %s\n", "test1.c", 5, "Dec 12 2001"); printf("%s(%d): %s\n", "test1.c", 6, "Dec 12 2001"); return 0; }<実行結果># ./a.out test1.c(4): Dec 12 2001 test1.c(5): Dec 12 2001 test1.c(6): Dec 12 2001