マクロの記述 - #define

マクロを定義して使用する方法について解説します。マクロとは、プリプロセッサの「#define」命令によって、ソースコードがコンパイルされる前に、ソースコード中に展開される文字列のことです。

#define マクロ名 文字列

マクロ名は、C言語の識別子で使うことができる文字と同じ文字を使うことができます。文字列は、任意の文字列で、空白を含むこともできます。

マクロには、いくつかの想定される利用用途があるので、紹介していきます。

マクロ定数

マクロの一つ目の用途は、定数定義です。ここでは、これを、マクロ定数と呼ぶことにします。

マクロ定数は、C言語の慣習的に、大文字で構成します。アンダースコアを含んでも構いません。

#define MYAPP_PI 3.14
#define MYAPP_PARALLEL_PROCESS 10

マクロ定数を、使用するサンプルプログラムです。「MYAPP_PI」は「3.14」に、コンパイルされる前に展開されます。

#include <stdio.h>

#define MYAPP_PI 3.14

int main(void) {
  double num = 3 * MYAPP_PI;
  
  printf("%f\n", num);
}

出力結果です。

9.420000

整数定数の場合はenumを使う

整数定数の場合は、マクロを使わず列挙型「enum」を使うことをお勧めします。enumはコンパイラによって処理され副作用がありません。

enum {
  MYAPP_PARALLEL_PROCESS = 10
}

マクロはできる限り使わない

プログラムを作成する場合は、できる限りマクロを使わないという方針で、作ることをお勧めします。

マクロ展開は、意図しない副作用を引き起こしやすいからです。

たとえば以下の副作用。

#define FOO 2 + 3

次の計算式の結果は?

1 * FOO * 3

次のように展開されます。

1 * 2 + 3 * 3

2 + 9 で 11 になります。FOOは、5であることを期待していたのではないでしょうか?

#define FOO (2 + 3)

と書くと正しく動きます。

また、関数プロトタイプ宣言の引数名が、マクロで展開されてしまうなど、予想しにくい副作用が発生します。

すべての副作用を書いていくことはしないのですが、マクロを使うと、間違いを引き起こしやすい副作用が、プログラムに入り込んでしまいがちということを、覚えておきましょう。

副作用は、プログラムが大きくなっていくにしたがって、発見箇所を追うのが、難しくなっていきます。そして、プログラムは、機能要望に応じて、大きくなっていきがちです。

「今日のちょっとしんどいけど、大丈夫だろう」は、未来の「すげーしんどい、もうだめなんだけど」につながっていきます。

マクロはいつ使うのか?

できる限り、使わないほうがよいというなら、それでは、マクロはいつ使うのか?

ひとつは、浮動小数点の定数定義ですね。

それ以外は?

たとえば、数値計算のあるライブラリが存在していることをチェックしたいとします。

そして、そのライブラリが存在していた場合は、パフォーマンスを速くできるなどの理由で、そのライブラリの機能を使うようにしたいとします。

このような場合は「#ifdef」とマクロを組み合わせて、記述することが、必須です。

一方で、どの環境でも動くような、C言語の限定した仕様だけを使ってポータブルなアプリケーションを作成しているとします。

このような場合は、浮動小数点の定数定義など、最小限のマクロ定義だけを使って、アプリケーションを構成できます。

このようなアプリケーションで、マクロが大量に使われていたとすると、何かが間違っている可能性が高いです。

一方、やはり、マクロがかなりの数、存在しなければならないアプリケーションもあります。それは、環境差を吸収するために作られたようなアプリケーションだったりします。たとえば、ユーザーから同じソケットAPIを見せるために、WindowsとLinux/UNIXのソケット通信の違いを吸収しなければならないなど。

またパフォーマンスを最速にしたいという条件があるので、コンパイラのインライン展開には期待せずに、手動で展開したいという場合も、マクロの利用が考えられます。

ですので、一概には言えないのですが、必要ではない場合はマクロは使わない、必要になったタイミングで、他に代替手段がないかをよく考えて使うということを、お勧めします。

関連情報