Cのマクロは、プログラマが定数、インラインコードスニペットを定義し、条件付きコンパイルを実行できるようにする、プリプロセッシングフェーズの強力な機能です。効果的に使用した場合、コードの再利用可能な保守性を大幅に向上させ、開発者がクリーナー、より柔軟なプログラムを書くことを可能にします。しかし、そのパワーはリスクに伴います。不適切なマクロ使用は、微妙なバグを導入し、コードをデバッグするのが困難です。この記事では、一般的な下肢の慣行を避けながら、マクロを最大限の利益のために活用する方法を学び、業界標準の慣行やCCIの慣行を経験します。

マクロとは?

マクロは、[] ディレクティブを使用して定義され、コンパイルが始まる前に C のプリプロセッサによって処理されます。 プリプロセッサーは、単純なテキスト置換を実行します。ソースコード内のマクロ名のすべての発生はマクロの定義に置き換えられます。 この置換は純粋にテキストであり、スコープや型チェックを尊重しません。 マクロは ] オブジェクトのような (定数のために使用されます) または [[FLT]: [FLT] オブジェクトの引数を引数に置き換えることができます。 [FLT:[FLT] 関数コードは、 [FLT] を引数に置き換えます。 [FLT:[FLT] 文字列] は、 [FLT: [FLT:[FLT] オブジェクトのコードは、 オブジェクトのコードは、 オブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクトのオブジェクト

#define PI 3.14159 // object-like macro
#define SQUARE(x) ((x)*(x)) // function-like macro

このプリプロセッシングステップを理解することは、マクロが関数や変数と同じルールに従わないため、重要なことです。 それらはインラインを拡大しており、パフォーマンス上の優位性を発揮しますが、ユニークな課題も紹介しています。

コードの信頼性とメンテナンスのためのマクロの利点

考慮して、マクロは、大または性能に敏感なコードベースでいくつかの具体的な利点を提供します。

  • [Code Reusability]: 共通計算またはスニペットを一度定義し、プログラム全体で使用してください。 マクロの更新をすべてのインスタンスを自動的に変更します。
  • ]:Magic Numbersの順調:やのような文字を散らばる代わりに、意味のある名前を与えます。 これは、読みやすくなり、パラメータをトバイアルに調整します。
  • [Performance Gains]:関数型マクロは関数呼び出しのオーバーヘッドを回避します。 小さくて頻繁に使用される操作(例えば、min/max、正方形)の場合、マクロはインライン関数よりも高速でインライン化され、インライン化が保証されていない特定のコンパイラでは機能がより速くなります。
  • [ 条件付きコンパイル]]:マクロは]、、および[を、コンパイル時条件に基づいてコードを含んだり、除外したりします。 これは、プラットフォーム固有のコード、デバッグビルド、または機能トグルに不可欠です。
  • [ は、コンフィギュレーションを簡素化しました。単一のヘッダで異なる値を持つマクロを定義することで、実装ファイルに触れることなく、コードベース全体で動作を適応させることができます。

強力なマクロを書く: 最高の練習

マクロの柔軟性も危険です。 書かれたマクロが悪いと、トレースが困難である予期しない動作を引き起こす可能性があります。 これらのベストプラクティスに従って、マクロを安全かつ維持することができます。

常にペアサイズパラメータとエンティア表現

マクロはテキスト置換であるため、演算子の優先順位はそれらを破ることができます。 []のようなマクロはをに拡大します(同等11、25ではなく)。 修正は括弧内のすべてのパラメータを囲み、全式をラップすることです。

#define SQUARE(x) ((x) * (x)) // now SQUARE(3+2) works correctly

引数の複数の評価を回避

マクロ引数はas-is を代入します。引数に副作用(例、)がある場合、複数回評価されることがあります。例えば:

#define MAX(a,b) ((a) > (b) ? (a) : (b))
int x = 5;
int max = MAX(x++, 10); // expands to ((x++) > (10) ? (x++) : (10)) // x incremented twice!

これを避けるために、インライン関数を好むか、警告するユーザーへの注意深いネーミング慣行を使用する。マクロを使う必要がある場合は、引数に副作用が含まれていなければいけません。

マクロ名用のアッパーケースを使用する

慣習によって、マクロは大文字(例、[、[])で書かれています。これにより、変数や関数から、誤った衝突のチャンスを減らすことができます。

複数の検出ブロックのためのD-Whileのエンド マクロ

複数のステートメントを含むマクロを定義するときは、[ ループでそれらをラップします。これにより、マクロは全てのコンテキスト(例えば、]の後に)正しく動作するようにします。括弧なし:

#define LOG_ERROR(msg) do { \
 fprintf(stderr, "Error: %s\n", msg); \
 exit(1); \
} while (0)

文書マクロ 徹底的に

マクロは機能の自己文書化の性質を欠いています。 常に引数、期待される行動、および任意の制限を記述するコメントを追加します。 適切なときに、Doxygenスタイルのコメントを使用してください。

高度なマクロ技術

C のプリプロセッサは、単純な置換を超えた強力な演算子を提供しています。これらをマスターすることで、マクロをより柔軟にすることができます。

で文字列化

演算子は、マクロ引数を文字列のリテラルに変換します。 これは、デバッグやロギングに役立ちます。

#define PRINT_VAR(x) printf(#x " = %d\n", x)
int value = 42;
PRINT_VAR(value); // prints: value = 42

でトークンを貼り付ける]

演算子は、2つのトークンを単一のトークンに連結します。これにより、コンパイル時に、識別子を動的に作成できます。

#define CREATE_VAR(prefix, index) prefix ## index
int CREATE_VAR(arr, 0) = 10; // becomes int arr0 = 10;

トークンペーストは、X マクロや繰り返しコードパターンを生成するために広く使われています。

バリスティックマクロ (C99)

C99 で導入された variadic マクロは、 を使用して引数の可変数を受け入れます。これにより、柔軟なロギングとデバッグマクロが可能になります。

#define DEBUG_PRINT(fmt, ...) \
 fprintf(stderr, "DEBUG: " fmt, __VA_ARGS__)
DEBUG_PRINT("x = %d, y = %d", x, y);

C11以降では、空の引数でコンマを追跡し、ポータビリティを向上させるために[を使うことができます。

一般的な落札とテムを避ける方法

経験豊富なC開発者がマクロ関連のバグに遭遇。これらの落とし穴を早期に認めることで頭痛を防ぎます。

  • [] セミコロンや括弧: セミコロンを追跡せずにブロックに拡大するマクロは、構文エラーを引き起こす可能性があります。 []は、パターンを解決します。
  • ]オペレータの優先サプライズ[: 上記のように常に括弧を刻む。
  • [] 関数を持つマクロをシェーディング:標準ライブラリ関数と同じ名前でマクロを定義すると、それは静かにそれをオーバーライドすることができます。 ユニークなネーミングまたは#undefを使用してください。
  • [非定着初期化[:オブジェクトのようなマクロは定数として使用されますが、それらはタイプされていません。 拡張自体が定数式である場合にのみ、定数式で使用することができます。
  • []難易度]をデバッグする:コンパイラがそれらを見る前にマクロが展開されるので、エラーメッセージは、マクロ名ではなく、拡張されたコードを参照します。 GCCまたはClang(例えば、[])の[フラグを使用して、トレースの問題にプリプロットされた出力を示します。

マクロの代替: const、enum、およびインライン関数

現代のCは、マクロの必要性をしばしば排除するより安全な代替手段を提供します。

  • []変数[:数値定数の場合、を選択します。 それらは適切なタイプチェックとスコープを尊重しています。 しかし、それらはすべてのコンテキスト(C90)の配列サイズなど)でコンパイル時の定数ではありません。 C99以降では、変数が偽定値である場合、は配列サイズに使用できます(例:)。
  • []]:関連する整数定数のセットでは、列はタイプセーフで、値を自動的に割り当てます。 それらは、エラーコードとフラグに広く使われます。
  • インライン関数]:C99で導入された[は、マクロのパフォーマンスをフルタイプの安全、デバッグサポート、および副作用の問題回避を提供します。 たとえば:
static inline int square(int x) {
 return x * x;
}

引数を正確に評価する関数型マクロを必要とするときは、インライン関数を使用します。関数型マクロの残りの使用例は、型(例えば、GNU C の型)で動作する必要があるとき、または新しいスコープを開く能力(do-while idiom のような)を必要とするときだけです。

実世界例:デバッグと構成

マクロは条件付きシナリオでエクセルを浴びています。以下は2つの実用的なアプリケーションです。

コンパイル時の制御によるデバッグログ

#ifdef DEBUG
 #define LOG(fmt, ...) fprintf(stderr, "[DEBUG] " fmt, ##__VA_ARGS__)
#else
 #define LOG(fmt, ...) /* no-op */
#endif

でコンパイルされたときのみ、このマクロはリリースビルドでゼロオーバーヘッドを引き起こします。 ]] の前に、引数が提供されていないときにトレースコンマを削除した GNU 拡張子 (また、C20 で ]) でサポートされています。

プラットフォーム固有のコード

#if defined(_WIN32) || defined(_WIN64)
 #define PLATFORM "Windows"
#elif defined(__linux__)
 #define PLATFORM "Linux"
#else
 #define PLATFORM "Unknown"
#endif

このパターンは、機能全体を複製することなくプラットフォームの依存関係を分離します。 [] ガードと組み合わせることで、ヘッダーをクリーンかつ維持できます。

コンテンツ

マクロは、コードの再利用可能なメンテナンスのために、特に、パフォーマンスクリティカルまたはすべてのサイクルがカウントする組み込みコンテキストで、C に不可欠なツールを維持します。 適切に使用されるとき、適切な括弧、限られた副作用、および適切なネーミングを使用して、重複と集中構成を削減します。 しかし、現代のC の傾向は、インライン関数やジェネリック (C11 で [)] を使用して、タイプ安全とデバッグ性を得るために、関数的なマクロを置き換えることです。 [FLT] および [FLT] マクロは、多くの場合、 [FLT] マクロは、 [FLT] マクロは、 [F] および [F] マクロは、 マクロは、 オブジェクトの比較対象の対象として [FLT] マクロは [F] マクロ [F] または [FLT] または [F] マクロは [FLT] または [F] または [F] または [F] の [F] マクロは、 [FLT マクロは、 マクロは、 [FLTF] マクロは、 [F] マクロの [F] オブジェクトの [FLT

これらのより安全な代替品で従来のマクロパワーをバランス良くすることで、Cコードを効率的かつ堅牢なものに書き出すことができます。さらに読み込むには、]]GNU C Preprocessor Manual]のマクロに関するCの参照を]を参照してください。マクロの下落についても、このにカバーされています。