PRE01-C. マクロ内の引数名は括弧で囲む
マクロ定義におけるすべての引数名は括弧で囲むこと。「PRE00-C. 関数形式マクロよりもインライン関数やスタティック関数を使う」と「PRE02-C. マクロ置換リストは括弧で囲む」も参照。
違反コード
以下の CUBE()
マクロ定義は、引数名を括弧で囲んでいない。
#define CUBE(I) (I * I * I)
そのため、次のような呼び出し
int a = 81 / CUBE(2 + 1);
は次のように展開される。
int a = 81 / (2 + 1 * 2 + 1 * 2 + 1); /* 11 に評価される */
これは明らかに、求めようとした結果ではない。
適合コード
CUBE()
マクロのすべての引数名を括弧で囲むことで、次のように呼び出されるときに正しく展開されるようになる。
#define CUBE(I) ( (I) * (I) * (I) ) int a = 81 / CUBE(2 + 1);
例外
PRE01-C-EX1: 置き換えられるテキスト中で引数名がコンマで囲まれている場合、実引数がいかに複雑であっても、マクロの引数に括弧をいれる必要はない。コンマは他のあらゆる演算子よりも優先順位が低いため、実引数が予期せぬ方法で解析されることはない。関数呼び出し時に引数を区切るコンマ区切りは、他の演算子よりも優先順位は低いが、厳密にはコンマ演算子とは異なるものである。
#define FOO(a, b, c) bar(a, b, c) /* ... */ FOO(arg1, arg2, arg3);
PRE01-C-EX2: マクロ引数を個別に括弧で囲むことができない場合として、##
演算子を用いて字句(token)を連結する場合、#
演算子を用いてマクロ引数を文字列に変換する場合、隣り合う文字列リテラルを連結する場合などがある。以下に示す JOIN()
マクロは 2 つの引数を連結して新しい字句を生成する。SHOW()
マクロでは、その引数を文字列リテラルに変換し、さらに隣接する文字列と連結した形で printf()
の第1引数とし、同時に %d
に対応する printf()
の第2引数としても展開している。たとえば、SHOW(66);
というコードは printf("66" " = %d\n", 66);
に展開される。
#define JOIN(a, b) (a ## b) #define SHOW(a) printf(#a " = %d\n", a)
##
演算子を用いた字句の連結に関する詳細は、「PRE05-C. 字句の結合や文字列化を行う際のマクロ置換動作をよく理解する」を参照のこと。
リスク評価
マクロ中の引数名を括弧で囲まないと、予期せぬプログラムの動作を引き起こす可能性がある。
レコメンデーション |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
PRE01-C |
中 |
中 |
低 |
P12 |
L1 |
自動検出(最新の情報はこちら)
ツール | バージョン | チェッカー | 説明 |
---|---|---|---|
ECLAIR | 1.2 | CC2.PRE01 | 実装済み |
9.5.6 |
78 S |
Enhanced Enforcement |
|
Parasoft C/C++test | 9.5 | MISRA2004-19_10 | Fully implemented |
PRQA QA-C | 8.2 | 3410 | 実装済み |
関連するガイドライン
SEI CERT C++ Coding Standard | PRE01-CPP. Use parentheses within macros around parameter names |
ISO/IEC TR 24772:2013 | Operator Precedence/Order of Evaluation [JCW] Pre-processor Directives [NMP] |
MISRA C:2012 |
Rule 20.7 (required) |
参考資料
[Plum 1985] | |
[Summit 2005] | Question 10.1 |
翻訳元
これは以下のページを翻訳したものです。
PRE01-C. Use parentheses within macros around parameter names (revision 149)