こんにちは。アサノです。
今回は自分の会社の環境で頻繁に使用されているマクロについて改めて調べてみました。
C++におけるマクロとは?
C++におけるマクロは、#defineを用いることで、コンパイル前にプリプロセッサで処理や文字列を指定したものに置き換える機能の事です。
中でも関数のように呼び出せるマクロは、複数の処理をまとめたい場合などに利用されます。
試しに記述してみると下記のようになります。
(こちらは後述する関数のように呼び出せる機能です。)
int coinBox = 0;
void coinAdd(){ coinBox++; }
void coinRemove(){ coinBox--; }
//マクロの定義
#define Macro() \
do { \
coinAdd(); \
coinRemove(); \
} while (0)
マクロの書き方の注意点
お恥ずかしながら私は学生時代にマクロを多用してきませんでした。
ですので、当初は下記のような記述方式を取っておりました。
#define Macro() \
coinAdd(); \
coinRemove()
では、なぜこの記述が良くないのでしょうか。
それは{}のついていないif文の仕様にあります。
if文は「直後の一文」つまりセミコロンがつくまでの文章しかコントロールしてくれないのです。
上記の記載方法で言うなら、coinAdd();までしかコントロール出来ないのです。
これが、{}の無いif文による使用を行うと、エラー、またはバグになってしまう原因です。
上記のように記述する際は、下記のように記述することで、
do{}while(0)までを一文として扱えるのでより安全になります。
#define Macro() \
do { \
coinAdd(); \
coinRemove(); \
} while(0)
また、マクロは関数の記述と違い、改行する場合には[ \ ]が必要なため、記載忘れに注意しましょう。自分も当初は驚いた覚えがあります。
マクロの使用方法について
マクロには大きく2つの使い方が存在します。
・1つは定数や文字列などを別の文字列に置き換えてくれる機能。
・2つ目は引数を受け取り、関数のように呼び出せる機能。
です。
試しに1つ目の式から記載してみようと思います。
こちらはオブジェクト形式マクロと呼ばれます。
#define num_A 10
int main()
{
int answer = 0;
answer = num_A;
return answer;
}
このように、#defineの後にマクロ名と置き換えたい値を記述することで、指定した文字列へ置き換えることができます。
この時、変数宣言時には必要なセミコロンが#defineによるマクロ宣言時には必要が無いため、間違えないようにしましょう。
続いては引数を受け取り、関数のように呼び出せる機能です。
こちらは、関数形式マクロと呼ばれる機能です。
例文は下記のようになります。
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int value = MAX(3, 5);
こちらも、基本的な記述方法は変わらず、#defineの後にマクロ名と引数を記述し、その後ろに展開したいコードを記述することで、指定したコードへ置き換えることができます。
またこのマクロは、実際に関数を呼び出すのではなく、コードが文字列として置き換えられるのが特徴となります。
以上です。
さて、少し話が脱線するのですが、関数形式マクロではマクロを作成する際に()をつけていますよね。
これには理由があります。下記の式をご覧ください。
#define NumBox 1 + 1
int result = NumBox * 2;
通常であれば上記の式の答えは4になります。
しかし、実際は下記のように処理されてしまうのです。
int result = 1 + 1 * 2;
// 算出される答えは3となる
これは、マクロが 文字の置き換え処理 であるというのが原因なのです。
文字で言い表すなら、変数のように計算して格納、結果が出た状態で計算してくれないのです。
ですので、上記のようなバグを防ぐために、基本的には下記のように()をつけて記述する・・というわけです。
#define NumBox (1 + 1)
int result = NumBox * 2;
//(1 + 1) * 2 になるので、答えは4となります。
以上です。
本記事は、業務や学習を通して理解した内容を、自分自身の整理も兼ねてまとめたものです。内容に改善点や補足などがありましたら、ご指摘いただけますと幸いです。