C言語のマクロはとても便利ですが、落とし穴も多くあります。このページではマクロの基本について説明します。
C言語におけるマクロとは、プログラム中の文字列をあらかじめ定義した規則にしたがって置換する機能のことをいいます。マクロは、#defineというプリプロセッサ指令により定義します。
プリプロセッサ指令とは、コンパイルに先立って行われるプリプロセス(前処理)を行うプログラムであるプリプロセッサに対する指示のことです。前処理指令やディレクティブとも呼ばれます。このプリプロセスでは、ソースコードに対して一定の規則に従った処理が施されます。マクロの置換もこのプリプロセスの一部となります。
マクロには、単純にプログラム中の文字列を指定した文字列に変換する「オブジェクト形式マクロ」と引数をとってあたかも関数のように使用できる「関数形式マクロ」の2種類があります。
オブジェクト形式マクロは、プログラム中の文字列を指定した文字列に変換するマクロです。オブジェクト形式マクロは、次の形式で定義します。
#define 文字列1 文字列2
このように定義すると、プリプロセスにおいて、プログラム中の文字列1を文字列2に置換します。簡単な例を見てみましょう。
/**
* @file ObjectLikeMacroSample.c
* @brief 簡単なオブジェクト形式マクロの例
*/
#include <stdio.h>
#define DATA_NUM 10 /* データの要素数 */
int data[DATA_NUM] =
{
92, 94, 90, 80, 81, 82, 84, 79, 75, 95
};
int main(int argc, char *argv[])
{
int i;
int total = 0;
for (i = 0; i < DATA_NUM; i++)
{
total += data[i];
}
printf("Total : %d¥n", total);
printf("Average : %d¥n", total / DATA_NUM);
return 0;
}
上記の例では、
#define DATA_NUM 10 /* データの要素数 */
というdefine定義により、プリプロセスでプログラム中のDATA_NUMという文字列が全て10という文字列に置き換わります。このマクロの利用法には、次の2つの利点があります。
1.保守性の向上(修正がしやすい)。データの要素数が10から変更になった場合、define定義のみを修正すればよい。
2.可読性の向上(プログラムが読みやすい)。プログラム中に10という数字が直接書かれているとその数字の意味がわからない。DATA_NUMと書かれていれば、データの個数を指していることがすぐにわかる。
関数形式マクロとは、マクロ置換を行う時に引数を取ることができ、あたかも関数のように使用できるマクロです。関数形式マクロは、次の書式で定義します。
#define マクロ名(引数の並び) 置き換えられる処理
簡単な例を見てみましょう。
/**
* @file FunctionLikeMacroSample.c
* @brief 簡単な関数形式マクロの例
*/
#include <stdio.h>
/**
* 最大値取得
* 与えられた2つの引数のうち大きい値を返す
* @param [in] a 値1
* @param [in] b 値2
* @retval 値1と値2のうち大きな方の値
*/
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int main(int argc, char *argv[])
{
printf("%d¥n", MAX(10, 11));
return 0;
}
上記の例では、MAX(10, 11)というプログラムが、プリプロセスにおいて、((10) > (11) ? (10) : (11)) に置換されます。その結果、このプログラムをコンパイル、実行すると、11と表示されます。
関数形式マクロは、マクロ置換が行われるので、関数呼び出し時のオーバーヘッドがない反面、多く使用されるとコードサイズが増加するという特徴を持ちます。
マクロが長い場合、複数行に分けて記述したいことがあります。その場合は、行の終わりにバックスラッシュ(\)をつけて改行します。この時、バックスラッシュの後には何を書いてもいけません。コメントや空白文字類も書くことはできません。
バックスラッシュは、開発している環境によっては、円記号(¥)と表示されることもあります。
マクロの有効範囲は、1つのコンパイル単位となります。1つのコンパイル単位とは、#include でインクルードしたファイルが展開された後の1つのファイルです。また、マクロは#defineで定義された以降の行で有効です。マクロの定義を無効にするには、#undefプリプロセッサ指令を使います。
以下は具体例です。11行目、19行目のコメントを外すとコンパイルエラーとなります。
/**
* @file MacroUndef.c
* @brief マクロの有効範囲を示す例
*/
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
// printf("%s¥n", HELLO_WORLD); // この位置ではコンパイルエラーとなる
#define HELLO_WORLD "Hello, world!"
printf("%s¥n", HELLO_WORLD);
#undef HELLO_WORLD
// printf("%s¥n", HELLO_WORLD); // この位置でもコンパイルエラーとなる
return EXIT_SUCCESS;
}
マクロの有効範囲は、1つのコンパイル単位であるため、複数のファイルで使用するマクロは、インクルード用のファイルに記述しておき、そのマクロを使用するファイルで、インクルード用のファイルをインクルードして使用するというのが一般的な使い方です。