C言語プリプロセッサ(CPP)の理解:コンパイルをシンプルにする仕組み
C言語プリプロセッサはコンパイラの一部ではなく、コンパイルプロセスにおける独立したステップです。簡単に言えば、Cプリプロセッサはテキスト置換ツールであり、実際のコンパイル前に必要な前処理を行うようコンパイラに指示します。ここではCプリプロセッサをCPPと呼びます。
プリプロセッサ指令
すべてのプリプロセッサ指令はハッシュ記号(#)で始まります。これは最初の非空白文字でなければならず、可読性のために最初の列から始めるのが慣例です。主なプリプロセッサ指令を以下に示します。
| No. | 指令と説明 |
|---|---|
| 1 | #define プリプロセッサマクロを置換定義します。 |
| 2 | #include 別ファイルのヘッダーを挿入します。 |
| 3 | #undef プリプロセッサマクロの定義を解除します。 |
| 4 | #ifdef 指定マクロが定義済みなら真を返します。 |
| 5 | #ifndef 指定マクロが未定義なら真を返します。 |
| 6 | #if コンパイル時条件が真か判定します。 |
| 7 | #else #ifの代替ブロックを指定します。 |
| 8 | #elif #elseと#ifを1文で記述します。 |
| 9 | #endif プリプロセッサ条件分岐を終了します。 |
| 10 | #error 標準エラー出力にエラーメッセージを出力します。 |
| 11 | #pragma 標準化された方法でコンパイラに特殊命令を発行します。 |
プリプロセッサの使用例
以下の例で各指令の動作を確認しましょう。
#define MAX_ARRAY_LENGTH 20
この指令により、CPPはMAX_ARRAY_LENGTHを20に置換します。定数定義に#defineを用いると可読性が向上します。
#include <stdio.h> #include "myheader.h"
1行目はシステムライブラリからstdio.hを取得し、現在のソースファイルに展開します。2行目はローカルディレクトリからmyheader.hを取得して展開します。
#undef FILE_SIZE #define FILE_SIZE 42
既存のFILE_SIZE定義を解除し、新たに42として定義します。
#ifndef MESSAGE #define MESSAGE "You wish!" #endif
MESSAGEが未定義の場合のみ定義します。二重定義を防ぐイディオムです。
#ifdef DEBUG /* デバッグ用ステートメント */ #endif
DEBUGが定義済みなら内部の文を処理します。コンパイル時に-DDEBUGフラグを渡せばデバッグコードを有効化でき、フラグを外せば無効化可能です。
事前定義マクロ
ANSI Cはいくつかのマクロを事前定義しています。これらはプログラミングで利用可能ですが、直接変更すべきではありません。
| No. | マクロと説明 |
|---|---|
| 1 | __DATE__ "MMM DD YYYY"形式の現在日付を文字リテラルで保持。 |
| 2 | __TIME__ "HH:MM:SS"形式の現在時刻を文字リテラルで保持。 |
| 3 | __FILE__ 現在のファイル名を文字列リテラルで保持。 |
| 4 | __LINE__ 現在の行番号を10進定数で保持。 |
| 5 | __STDC__ コンパイラがANSI標準準拠なら1と定義。 |
以下の例で動作を確認します。
#include <stdio.h>
int main() {
printf("File :%s\n", __FILE__ );
printf("Date :%s\n", __DATE__ );
printf("Time :%s\n", __TIME__ );
printf("Line :%d\n", __LINE__ );
printf("ANSI :%d\n", __STDC__ );
}
test.cとして保存しコンパイル・実行すると、以下のような出力になります。
File :test.c Date :Jun 2 2012 Time :03:36:24 Line :8 ANSI :1
プリプロセッサ演算子
Cプリプロセッサはマクロ作成を助ける以下の演算子を提供します。
マクロ継続(\)演算子
マクロは通常1行に収めますが、長すぎる場合はバックスラッシュ(\)で行継続できます。
#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n")
文字列化(#)演算子
マクロ定義内で#を用いると、マクロパラメータを文字列定数に変換できます。引数リストを持つマクロでのみ使用可能です。
#include <stdio.h>
#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n")
int main(void) {
message_for(Carole, Debra);
return 0;
}
実行結果:
Carole and Debra: We love you!
トークン連結(##)演算子
マクロ定義内で##を用いると、2つの引数を1つのトークンに結合できます。
#include <stdio.h>
#define tokenpaster(n) printf("token" #n " = %d", token##n)
int main(void) {
int token34 = 40;
tokenpaster(34);
return 0;
}
実行結果:
token34 = 40
プリプロセッサ展開後は以下のようになります。
printf("token34 = %d", token34);
ここではtoken##nがtoken34に結合され、文字列化とトークン連結の両方が使われています。
defined()演算子
プリプロセッサのdefined演算子は、識別子が#defineで定義済みかを定数式で判定します。定義済みなら真(非ゼロ)、未定義なら偽(ゼロ)です。
#include <stdio.h>
#if !defined(MESSAGE)
#define MESSAGE "You wish!"
#endif
int main(void) {
printf("Here is the message: %s\n", MESSAGE);
return 0;
}
実行結果:
Here is the message: You wish!
引数付きマクロ(パラメータ化マクロ)
CPPの強力な機能の一つは、引数付きマクロで関数を模倣できることです。例えば、数値の二乗を計算する関数は以下のように書けます。
int square(int x) {
return x * x;
}
これをマクロで書き換えると:
#define square(x) ((x) * (x))
引数付きマクロは#defineで定義し、引数リストはカッコで囲み、マクロ名と開きカッコの間にスペースを入れないのがルールです。
#include <stdio.h>
#define MAX(x,y) ((x) > (y) ? (x) : (y))
int main(void) {
printf("Max between 20 and 10 is %d\n", MAX(10, 20));
return 0;
}
実行結果:
Max between 20 and 10 is 20
C言語
- C# Hello World – 初めてのC#アプリケーション構築
- C# キーワードと識別子:ルール、一覧、ベストプラクティス完全ガイド
- C# 変数とプリミティブデータ型:完全マスターガイド
- C#演算子完全ガイド:C#における演算子の包括的解説
- C# ビット演算子とビットシフト演算子をマスターする:包括的ガイド
- C# 入出力の基礎:ユーザー入力と出力の基本をマスター
- C# の式、文、ブロック:実践例で徹底理解
- C# コメントをマスターする:種類、ベストプラクティス、XML ドキュメント
- C# switch 文をマスターする:構文、例、ベストプラクティス
- C# 三項演算子:条件式を簡潔に書くためのガイド
- C# break 文:使い方とタイミングを徹底解説