STEP 9 - プリプロセッサ │ ├ プリプロセス ├ マクロ ├ 簡単な関数のようなマクロ ├ #undef句 ├ 三項演算子 ├ 処理系が定義しているマクロ │ ├ __TIME__ │ ├ __DATE__ │ ├ __FILE__ │ └ __LINE__ ├ 条件付きコンパイル │ ├ #if〜#endif │ ├ #elif │ ├ #else │ ├ #if defined │ ├ #if !defined │ ├ #ifの使用例 1 │ └ #ifの使用例 2 └ ゲーム作成時での使用例 |
Written by Yuki. http://ftc.suki.net/ |
プリプロセッサとはコンパイルの手順の 1つで、インクルード句やマクロ等のことを指す。 コンパイルという作業にはプリプロセス(前処理)、コンパイル(翻訳)があり、プリプロセッサは前処理で仕事をする。
<プリプロセス> 翻訳に必要なものを準備する。include や define 等、頭に # が付くものが それを行うプリプロセッサである。これにはセミコロンは付かない。
<マクロ> マクロは文字列や数字の置き換えと定義をする。マクロは大文字で書く。 #define 文字列1 文字列2 プリプロセス時に文字列1を文字列2に置き換える。 #difine SIZE 5 ' SIZEを定数5に置き換える。これをマクロ定数定義という。 変数や関数と区別し易くするため大文字で記述する。 #include <stdio.h> #define PRINT(a) printf("%d\n",(a)) ' PRINT(a)が文字列1。それ以降の式が文字列2である。 void main(){ PRINT(100); } ' printf("%d\n",100)に置き換えられる。 長い文やスペルの長い語句を繰り返し記述・入力する時に便利。 マクロはデータ型や定数等、色々なものに対応できる。
<簡単な関数のようなマクロ> #define f(a,b) a*b ' fに 2つの数値を入力させ、それを乗算するマクロ。 ans=f(2,2); ' 2*2で ansには 4が入る。 ans=f(2+3,3); ' マクロは単純に置き換えるだけなので、この場合は a=2+3,b=3 にはならず、2+3*3 で ansは 11となる。 四則演算に則って計算するので、意図した結果と異なる場合がある。 それを防止する為に、宣言を少し変える必要がある。 #define f(a,b) (a)*(b) ans=f(2+3,3); ' (2+3)*(3)で 15になる。但し、場合によっては 括弧を付けるだけでは対処できない事もある。
<#undef句> ・defineで定義された記号定数やマクロの定義を取り消す。 #define SIZE 500 printf("%d\n",SIZE); ' 500を出力。 #undef SIZE ' SIZEの中身を消去。 printf("%d\n",SIZE); ' 未定義でエラーが出る。 #define SIZE 1000 ' SIZEを再定義。再定義は undefが無くても出来なくはない。 ・ヘッダファイル内のマクロ定義を取り消す、又は再定義する。 #include <stdio.h> #include <stdlib.h> ' RAND_MAX(32767)が入っている。 #undef RAND_MAX ' RAND_MAXを取り消す。 #define RAND_MAX 127 ' 127に再定義。 printf("%d\n",RAND_MAX);
<三項演算子> if〜eleseと同じ機能を持つ。 a = 条件式? 式1 : 式2; ' 式1は真・式2は偽であり 条件式が真なら aに式1が、偽なら式2が代入される。 #define MAX(x,y) (((x)>(y)?(x):(y))) #define MIN(x,y) (((x)<(y)?(x):(y))) void main() { int i,j; scanf("%d",&i); scanf("%d",&j); printf("Max:%d\n",MAX(i,j)); ' i,j がそれぞれ x,y に対応する。 printf("Min:%d\n",MIN(i,j)); ' iが大きければ iを出力。そうでなければ jを出力(MINではその逆)。 } ※マクロ定義の時は括弧の付け方に注意する。括弧が多い、又は少ない等、数が合っていなくてもエラーは出ない。 但し、マクロを使用する時にバランスが取れるように、括弧の数を合わせないと実行時にエラーになる。 #define MUL(a,b) ((a)*(b) ' 定義の時点で括弧の組み合わせが一つ足りない。 printf("%d\n",MUL(2,3))); ' 記述時に括弧を余計に一つ付けておけば、括弧ごと置き換えて、 ((2)*(3))となって計算式が成立する。
<処理系が定義しているマクロ> 初めから定義されているマクロで、内蔵マクロという。 (1)__TIME__ ' 前後にアンダースコアが 2つずつ付く。 ビルドした時間を「時:分:秒」の書式の文字列で返す。 ただ、表示される時刻はあくまでビルド時のもので、 関数が呼ばれた瞬間の時刻ではない。 (2)__DATE__ ビルドした日付を「月:日:年」の書式の文字列で返す。 但し、月は英語の暦に則り Jul(7月),Aug(8月) という形で表示する。 (3)__FILE__ ビルドしたファイルのファイル名を返す。カレントパス名も一緒に返す。 つまり、ディレクトリ・フォルダ・ファイルと順に書かれる。 (4)__LINE__ プログラムの上から何行目かを整数 10進数で返す。先頭の行が 1である。
<条件付きコンパイル> 条件により コンパイルする・しない を部分的に設定する。 (1)#if〜#endif 定数式が真ならば #if と #endif で挟まれた文の並びをコンパイルする。偽ならコンパイルしない。 #if 定数式 ' #if 1→真(0以外)「文の並び」をコンパイルする。 文の並び ' #if 0→偽 コンパイルしない。 #endif ' #ifの終わりを示す。 ※定数式に変数は使えない。定数・定数定義されたマクロを使用できる。 (2)#elif 定数式1が偽で、2が真ならばコンパイルする。 (3)#else 全ての定数式が偽ならば、#else以下の文2をコンパイルする。 #if 定数式1 文1 ' #if 1なら文1のみをコンパイル。 #elif 定数式2 文2 ' #if 0で #elif 1なら文2のみをコンパイル。 #else 文3 ' #if 0,#elif 0なら文3のみをコンパイル。 #endif (4)#if defined マクロ名が定義されていれば、それ以降の文をコンパイルする。 (5)#if !defined マクロ名が定義されていなければ、それ以降の文をコンパイルする。 #if defined マクロ名 #if !defined マクロ名 (#ifの使用例 1) → サンプルプログラム 文字列をコピーする定義関数を作成する。 その中で、 @引数の受け渡し A文字のコピー状態 B戻り値の受け渡し をチェックする。 #include <stdio.h> #define DEBUG 0 ' マクロ定義 char *StrCpy2(char *,char *); ' サブルーチンのプロトタイプ宣言。 void main(){ char str00[]="abcd"; ' str00 は "abcd"。 char str01[256],*sp; ' str01 はポインタ配列。 #if defined DEBUG ' デバッグが定義されていれば、以下の文を実行。 printf("%d\n",str00); printf("%d\n",str01); #endif ' ここまで。 sp=StrCpy2(str01,str00); ' サブルーチンを呼ぶ。 #if defined DEBUG printf("%d\n",sp); ' 定義されていれば spを数値で表示。 #endif puts(sp); ' spを表示。 } char *StrCpy2(char *s1,char *s2){ ' サブルーチン。 #if defined DEBUG puts("DEBUG"); printf("%d\n",s1); printf("%d\n",s2); int count=0; #endif #if !defined DEBUG ' デバッグが定義されていなければ "NORMAL" と表示。 puts("NORMAL"); #endif char *rp=s1; do{ *s1=*s2; #if defined DEBUG putchar(*s1); putchar(*s2); printf("%d\n",count++); ' s1 に s2 をコピーし、カウントを増やして表示。 #endif s1++; s2++; }while(*s2); #if defined DEBUG printf("%d\n",rp); #endif return(rp); } (#ifの使用例 2) → サンプルプログラム 文字列をコピーする定義関数を作成する。 その中でチェックレベルにより、表示する項目を変更する。 ※下のソースは省いて表記してあるので、そのままコンパイルしてもエラーが出ます。 考え方のみ参考にして下さい。↑のサンプルプログラムが完全版です。 レベル 1:引数の受け渡し レベル 2:引数の受け渡し・コピーの状態 レベル 3:引数・戻り値の受け渡し・コピーの状態 #include <stdio.h> #define DEBUG 1 ' 数値によりレベルを切り換える。 char *StrCpy2(char *,char *); ' プロトタイプ宣言。 void main() { . . . #if DEBUG == 1 puts("LEVEL 1"); #elif DEBUG == 2 puts("LEVEL 2"); #elif DEBUG == 3 puts("LEVEL 3"); #else puts("NORMAL"); #endif ' デバッグの値ごとに表示を変更。 . . . #if DEBUG >= 1 ' レベル 1〜3 でコンパイルされる。 printf("%d\n",str00); printf("%d\n",str01); #endif #if DEBUG >= 2 ' 2〜3 でコンパイル。 putchar(*s1); putchar(*s2); printf("%d\n",count++); #endif #if DEBUG >= 3 ' 3 でのみコンパイル。 printf("%d\n",rp); #endif . . . }
<ゲーム作成時での使用例> ・当たり判定のチェック ' プレイヤーの座標(x,y) ・イベント発生のチェック ' 当たり判定の座標(x1,x2,y1,y2) ・キャラクターの動作のチェック ' 敵キャラの座標(ex,ey) ※デバッグの際に上記の様な数値等を表示して確認する。
← ← Back to BeforeStep | Go to NextStep → → |