defineはC言語でのプリプロッセッサへの指示のひとつです。プリプロッセッサとは、コンパイルの前に前処理を行うプログラムのことです。下記の構文で記述すると、コンパイル時に前処理として、文字列1を文字列2に変換します。このような変換をマクロ置換と呼びます。
C言語のdefineについて、主なdefineの使い方、記号定数のdefine、関数マクロ、条件付き取り込みについて解説しています。
defineはC言語でのプリプロッセッサへの指示のひとつです。プリプロッセッサとは、コンパイルの前に前処理を行うプログラムのことです。下記の構文で記述すると、コンパイル時に前処理として、文字列1を文字列2に変換します。このような変換をマクロ置換と呼びます。
#define 文字列1 文字列2
C言語でのdefineの使い方でよく使われるものは、次の3つです。
これらを順に説明します。
ソース上に、意味のある定数を10や25など直接の数値、いわゆるマジック・ナンバーとして書いてしまうのはよいことではありません。後でプログラムを読む人がこの数値の意味を理解できませんし、数値が変更になった場合、ソースのいたるところを修正しなければなりません。
これを避けるために、defineをよく使用します。例えば、下記のように上限値と下限値を記号定数として定義し、使用すれば、ソースを読む時にその意味がわかりますし、上限値、下限値が変更になった場合、defineしたところだけ変えれば、変更が全てのソースに反映されます。
#define LOWER 12
#define UPPER 348
defineによるマクロ置換は、引数付きにすることができます。これは関数マクロと呼ばれています。一見すると関数を呼び出しているように見えます。以下の書式で定義します。
#define マクロ名(引数) 処理
実際の例を見てみましょう。次のマクロは、2つの引数を取り小さい方を返します。
#define MIN(a, b) ((a) < (b) ? (a) : (b))
ソース中にMIN()が現れると、プリプロセッサが自動的に変換します。変換前と変換後の例を示します。
x = MIN(n1, n2);
x = ((n1) < (n2) ? (n1) : (n2));
一般的に、関数マクロは、関数呼び出しのオーバーヘッドを避けるために使用します。
関数マクロを使用する際には、注意が必要です。例えば、上記のMIN()で引数にn1++, n2++を渡すと小さい方の数が2回インクリメントされてしまいます。
また、次の例のように括弧をつけないと、意図したとおりに動かないことになります。
#define CALC_TRIANGLE_AREA(b, a) b*a/2
/* 中略 */
x = CALC_TRIANGLE_AREA(base+100, altitude-50);
/*
展開結果が、x = base+100*altitude-50/2;
となり、意図した結果とならない。
*/
ヘッダ・ファイルの二重読み込みを防ぐために、ヘッダ・ファイル内に読み込み条件をつける場合があります。この時によくdefineが使われます。
例えばsample.hというヘッダ・ファイルを次のような内容にすると二重読み込みを防げます。
#ifndef SAMPLE
#define SAMPLE
/* sample.hの内容をここに書く */
#endif
#ifndefは「もしdefineされていなければ」の意味です。このように記述しておけば、最初にsample.hを取り込んだ時にSAMPLEがdefineされ、次にsample.hを取り込む時は既にSAMPLEがdefineされているので#endifまで読み込みがスキップされ、二重読み込みが回避されます。これで複数のファイルでsample.hをインクルードしても実際に読み込まれるのは1回だけとなり、たいへん便利です。