
C言語入門|#define とマクロ定数のしくみ
― コンパイル前に行われる「文字列の翻訳」
プリプロセッサには、インクルード処理のほかにも重要な仕事があります。
その代表例が #define によるマクロ処理 です。
#include が「ファイルをその場に展開する命令」だったのに対し、
#define は ソースコード中の文字列を、別の文字列に置き換える命令 です。
この置換はコンパイル前に行われるため、
コンパイラは「置換後のコード」だけを見て処理します。
この仕組みを理解すると、
- なぜ昔のC言語では #define が多用されていたのか
- なぜ今は使いどころに注意が必要なのか
が、自然に見えてきます。

#define プリプロセッサ命令の基本形
#define の書式はとてもシンプルです。
#define 置換前の文字列 置換後の文字列プリプロセッサは、
ソースコード中に現れる「置換前の文字列」をすべて探し、
そのまま「置換後の文字列」に入れ替えます。
計算も型チェックも行いません。
ただの文字列置換です。
マクロ定数の例
まずは、最もよく使われる「マクロ定数」の例を見てみましょう。
サンプルプログラム
プロジェクト名:13-5-1 ソースファイル名: sample13-5.1.c
#include <stdio.h>
#define TAX_RATE 0.1
int main(void)
{
int price = 1200;
int total = price + price * TAX_RATE;
printf("total = %d\n", total);
return 0;
}このコードでは、TAX_RATE という名前を使っていますが、
コンパイル前にプリプロセッサが次のように置き換えます。
int total = price + price * 0.1;TAX_RATE という名前の変数が存在しているわけではありません。
単に文字列が置き換えられているだけです。

マクロ定数は「定数」ではない
見た目は定数のようですが、
マクロ定数は C言語の型システムとは一切関係ありません。
たとえば、次のような記述も可能です。
#define TAX_RATE "0.1"この行自体ではエラーになりません。
エラーが出るとしたら、展開された先のコードです。
つまり、
- 定義した場所では問題なし
- 使った場所で突然エラー
という状況が起こり得ます。
これが、マクロ定数が扱いにくい理由の1つです。
副作用① 型や構文のチェックが効かない
#define は、コンパイラより前に処理されます。
そのため、
- 型チェック
- 構文チェック
は一切行われません。
マクロ定数は、
安全性よりも手軽さを優先した仕組み だと考えるとよいでしょう。
大規模なプログラムでは、
思わぬ場所でエラーが発生し、原因が追いにくくなることもあります。
副作用② 危険な置換ができてしまう
マクロは「単なる文字列置換」なので、
極端な話、予約語や関数名すら置き換えられます。
#define main game_startと書けば、ソースコード中の main はすべて game_start に置き換えられます。
これは意図的に使えば強力ですが、
意図せず行うと非常に危険です。
そのため、
- 定数として使いたいだけ
- 型安全に扱いたい
という場合は、通常の定数宣言を使うほうが安全です。
const double TAX_RATE = 0.1;NULL も実はマクロだった
これまで「ポインタが何も指していない状態」として使ってきた NULL も、
歴史的には #define で定義されたマクロ定数です。
処理系によって異なりますが、
次のような定義を見つけられることがあります。
#define NULL ((void*)0)つまり、NULL も
「特別な値」ではなく
プリプロセッサによる置換結果にすぎません。
定義済みマクロ定数
C言語には、開発者が定義しなくても
最初から用意されているマクロ定数があります。
これらは主にデバッグやログ出力で使われます。
| マクロ名 | 意味 |
|---|---|
| FILE | ソースファイル名 |
| LINE | 行番号 |
| DATE | プリプロセッサ実行日 |
| TIME | プリプロセッサ実行時刻 |
| func | 関数名(厳密にはマクロではない) |
これらもすべて、
コンパイル前に文字列として展開されるものです。
マクロ定数との付き合い方
マクロ定数は、
- 古くから使われてきた。
- 処理が軽く。
- コンパイル後のコードに影響を残さない。
という利点があります。
一方で、
- 型安全ではない。
- デバッグが難しい。
という欠点もあります。
そのため現在では、
- 定数 → const や enum
- 処理の抽象化 → 関数
を使い、
#define は「本当に必要な場面」に絞って使うのが一般的です。
