導入
同じ数値(たとえば消費税率や配列のサイズ)をコードのあちこちに直接書いていると、後で変更するときに1か所でも直し忘れて不具合が起きます。#define を使えば、その値に名前を付けて一元管理できます。
説明
#define 名前 値 と書くと、以降のコードに出てくる 名前 が、コンパイルされる前に文字どおりその 値 に置き換えられます。関数のように呼び出せる「関数マクロ」も作れます。
#include <stdio.h>
#define PI 3.14
#define SQUARE(x) ((x) * (x))
int main(void) {
printf("円周率は %f\n", PI);
printf("5の2乗は %d\n", SQUARE(5));
return 0;
}
PI は単純に 3.14 という文字列に置き換わるだけです。SQUARE(x) は「引数を受け取る」ように見えますが、これもプリプロセッサによる文字の貼り替えにすぎません。SQUARE(5) は ((5) * (5)) にそのまま置き換わります。
flowchart LR src["ソースコード<br/>SQUARE(5)"] -->|"プリプロセッサが文字を置換"| exp["((5) * (5))"] exp -->|"この形でコンパイル"| bin["実行ファイル"]
ここで注目してほしいのが SQUARE(x) の定義に付いている括弧の多さです。(x) と、全体を囲む ( ) の両方が必要です。理由は「ただの文字の貼り替え」だから起こる事故を防ぐためです。次のように括弧を省略すると、思わぬ結果になります。
#include <stdio.h>
#define BAD_SQUARE(x) x * x
int main(void) {
printf("%d\n", BAD_SQUARE(2 + 3)); // 期待は25だが…
return 0;
}
BAD_SQUARE(2 + 3) は 2 + 3 * 2 + 3 に置き換わります。掛け算は足し算より先に計算されるので、2 + 6 + 3 = 11 という、意図しない値になってしまいます。SQUARE(x) のように (x) と全体を ( ) で囲んでいれば、((2 + 3) * (2 + 3)) となり、正しく 25 になります。マクロの引数と全体は必ず括弧で囲む、という習慣を付けましょう。
やってみよう
SQUARE(5) を SQUARE(2 + 3) に変えて 25 になることを確かめましょう。BAD_SQUARE の例も実行して、括弧を省略した場合の結果と比べてみてください。
演習
値を2倍にするマクロ DOUBLE(x) を、括弧を正しく使って定義してください。int n = 7; に対して DOUBLE(n) を printf で表示してください(結果は 14)。
ヒント1を見る
マクロ定義は #define DOUBLE(x) ((x) * 2) のように書きます。
ヒント2を見る
呼び出しは普通の関数のように DOUBLE(n) と書けます。