STEP 9 - プリプロセッサ
 │
 ├ プリプロセスマクロ簡単な関数のようなマクロ#undef句三項演算子処理系が定義しているマクロ
 │ ├ __TIME__
 │ ├ __DATE__
 │ ├ __FILE__
 │ └ __LINE__条件付きコンパイル
 │ ├ #if〜#endif
 │ ├ #elif
 │ ├ #else
 │ ├ #if defined
 │ ├ #if !defined
 │ ├ #ifの使用例 1
 │ └ #ifの使用例 2ゲーム作成時での使用例
猿でも解る C言語講座

Written by Yuki.
http://ftc.suki.net/
プリプロセッサとはコンパイルの手順の 1つで、インクルード句やマクロ等のことを指す。
コンパイルという作業にはプリプロセス(前処理)、コンパイル(翻訳)があり、プリプロセッサは前処理で仕事をする。


<プリプロセス>  翻訳に必要なものを準備する。includedefine 等、頭に # が付くものが  それを行うプリプロセッサである。これにはセミコロンは付かない。
<マクロ>  マクロは文字列や数字の置き換えと定義をする。マクロは大文字で書く。  #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 → →